■ USB通信2(VC++)
■ PIC18F14K50 HIDクラス LED 点灯/消灯 制御・表示
<試作品仕様>
・PC(Windows)上のボタンスイッチ×4個をクリックすると PIC18F40K50のキバン上の対応するLEDがON/OFFすること
・PCとPIC間のUSB通信はHIDクラスとする。
・ボタンスイッチはLEDがONしているときは赤色、OFFの時は灰色とする。
・USB通信ONのボタンボタンスイッチをフォーム上にもうけること。 またUSB通信が確立したら”接続完了!”の文字をテキストボックスに表示のこと
★ ハード 及びPIC側のソフトは こちらを参照願います
<プログラム例> //Form1.h #pragma once //Includes #include <Windows.h> //Definitions for various common and not so common types like DWORD, PCHAR, HANDLE, etc. #include <setupapi.h> //From Platform SDK. Definitions needed for the SetupDixxx() functions, which we use to //find our plug and play device. //Modify this value to match the VID and PID in your USB device descriptor. //Use the formatting: "Vid_xxxx&Pid_xxxx" where xxxx is a 16-bit hexadecimal number. #define MY_DEVICE_ID "Vid_04d8&Pid_003F" //VID = 0x04D8 PID = 0x003F namespace CWhiteForm { using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; //-------------------------------------------------------BEGIN CUT AND PASTE BLOCK-------------------------- using namespace System::Runtime::InteropServices; //Need this to support "unmanaged" code. /* In order to use these unmanaged functions from within the managed .NET environment, we need to explicitly import the functions which we will be using from other .DLL file(s). Simply including the appropriate header files is not enough. Note: In order to avoid potential name conflicts in the header files (which we still use), I have renamed the functions by adding "UM" (unmanaged) onto the end of them. To find documentation for the functions in MSDN, search for the function name without the extra "UM" attached. Note2: In the header files (such as setupapi.h), normally the function names are remapped, depending upon if UNICODE is defined or not. For example, two versions of the function SetupDiGetDeviceInterfaceDetail() exist. One for UNICODE, and one for ANSI. If the wrong version of the function is called, things won't work correctly. Therefore, in order to make sure the correct one gets called (based on your compiler settings, which may or may not define "UNICODE"), it is useful to explicity specify the CharSet when doing the DLL import. */ #ifdef UNICODE #define Seeifdef Unicode #else #define Seeifdef Ansi #endif //使用するDLL関数の宣言 //Returns a HDEVINFO type for a device information set (USB HID devices in //our case). We will need the HDEVINFO as in input parameter for calling many of //the other SetupDixxx() functions. [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiGetClassDevs", CallingConvention=CallingConvention::Winapi)] extern "C" HDEVINFO SetupDiGetClassDevsUM( LPGUID ClassGuid, //Input: Supply the class GUID here. PCTSTR Enumerator, //Input: Use NULL here, not important for our purposes HWND hwndParent, //Input: Use NULL here, not important for our purposes DWORD Flags); //Input: Flags describing what kind of filtering to use. //Gives us "PSP_DEVICE_INTERFACE_DATA" which contains the Interface specific GUID (different //from class GUID). We need the interface GUID to get the device path. [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiEnumDeviceInterfaces", CallingConvention=CallingConvention::Winapi)] extern "C" WINSETUPAPI BOOL WINAPI SetupDiEnumDeviceInterfacesUM( HDEVINFO DeviceInfoSet, //Input: Give it the HDEVINFO we got from SetupDiGetClassDevs() PSP_DEVINFO_DATA DeviceInfoData, //Input (optional) LPGUID InterfaceClassGuid, //Input DWORD MemberIndex, //Input: "Index" of the device you are interested in getting the path for. PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData);//Output: This function fills in an "SP_DEVICE_INTERFACE_DATA" structure. //SetupDiDestroyDeviceInfoList() frees up memory by destroying a DeviceInfoList [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiDestroyDeviceInfoList", CallingConvention=CallingConvention::Winapi)] extern "C" WINSETUPAPI BOOL WINAPI SetupDiDestroyDeviceInfoListUM( HDEVINFO DeviceInfoSet); //Input: Give it a handle to a device info list to deallocate from RAM. //SetupDiEnumDeviceInfo() fills in an "SP_DEVINFO_DATA" structure, which we need for SetupDiGetDeviceRegistryProperty() [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiEnumDeviceInfo", CallingConvention=CallingConvention::Winapi)] extern "C" WINSETUPAPI BOOL WINAPI SetupDiEnumDeviceInfoUM( HDEVINFO DeviceInfoSet, DWORD MemberIndex, PSP_DEVINFO_DATA DeviceInfoData); //SetupDiGetDeviceRegistryProperty() gives us the hardware ID, which we use to check to see if it has matching VID/PID [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiGetDeviceRegistryProperty", CallingConvention=CallingConvention::Winapi)] extern "C" WINSETUPAPI BOOL WINAPI SetupDiGetDeviceRegistryPropertyUM( HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, DWORD Property, PDWORD PropertyRegDataType, PBYTE PropertyBuffer, DWORD PropertyBufferSize, PDWORD RequiredSize); //SetupDiGetDeviceInterfaceDetail() gives us a device path, which is needed before CreateFile() can be used. [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiGetDeviceInterfaceDetail", CallingConvention=CallingConvention::Winapi)] extern "C" BOOL SetupDiGetDeviceInterfaceDetailUM( HDEVINFO DeviceInfoSet, //Input: Wants HDEVINFO which can be obtained from SetupDiGetClassDevs() PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, //Input: Pointer to an structure which defines the device interface. PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData, //Output: Pointer to a strucutre, which will contain the device path. DWORD DeviceInterfaceDetailDataSize, //Input: Number of bytes to retrieve. PDWORD RequiredSize, //Output (optional): Te number of bytes needed to hold the entire struct PSP_DEVINFO_DATA DeviceInfoData); //Output // Variables that need to have wide scope. HANDLE WriteHandle = INVALID_HANDLE_VALUE; //Need to get a write "handle" to our device before we can write to it. HANDLE ReadHandle = INVALID_HANDLE_VALUE; //Need to get a read "handle" to our device before we can read from it. //-------------------------------------------------------END CUT AND PASTE BLOCK------------------------------------------------------------------------------------- //グローバル変数定義 // unsigned int ADCValue; int LedMode = 0, // LedMode = 0; 消灯 1:点灯 Led1Mode = 0, // Led1Mode = 0; 消灯 1:点灯 Led2Mode = 0, // Led2Mode = 0; 消灯 1:点灯 Led3Mode = 0; // Lee3Mode = 0; 消灯 1:点灯 DWORD BytesWritten = 0; DWORD BytesRead = 0; unsigned char OutputPacketBuffer[65]; //Allocate a memory buffer equal to our endpoint size + 1 unsigned char InputPacketBuffer[65]; /// <summary> /// Form1 の概要 /// </summary> public ref class Form1 : public System::Windows::Forms::Form { public: Form1(void) //コンストラクタ { InitializeComponent(); // //TODO: ここにコンストラクター コードを追加します // } protected: /// <summary> /// 使用中のリソースをすべてクリーンアップします。 /// </summary> ~Form1() //デストラクタ { if (components) { delete components; } } private: System::Windows::Forms::Button^ ConnectBtn; private: System::Windows::Forms::Button^ LedSw; protected: private: System::Windows::Forms::TextBox^ textBox1; private: System::Windows::Forms::Button^ LedSw1; private: System::Windows::Forms::Button^ LedSw2; private: System::Windows::Forms::Button^ LedSw3; private: /// <summary> /// 必要なデザイナー変数です。 /// </summary> System::ComponentModel::Container ^components; #pragma region Windows Form Designer generated code /// <summary> /// デザイナー サポートに必要なメソッドです。このメソッドの内容を /// コード エディターで変更しないでください。 /// </summary> void InitializeComponent(void) //コンポーネントの初期化 { this->ConnectBtn = (gcnew System::Windows::Forms::Button()); this->LedSw = (gcnew System::Windows::Forms::Button()); this->textBox1 = (gcnew System::Windows::Forms::TextBox()); this->LedSw1 = (gcnew System::Windows::Forms::Button()); this->LedSw2 = (gcnew System::Windows::Forms::Button()); this->LedSw3 = (gcnew System::Windows::Forms::Button()); this->SuspendLayout(); // // ConnectBtn // this->ConnectBtn->Location = System::Drawing::Point(35, 24); this->ConnectBtn->Name = L"ConnectBtn"; this->ConnectBtn->Size = System::Drawing::Size(83, 33); this->ConnectBtn->TabIndex = 0; this->ConnectBtn->Text = L"接続"; this->ConnectBtn->UseVisualStyleBackColor = true; this->ConnectBtn->Click += gcnew System::EventHandler(this, &Form1::ConnectBtn_Click); // // LedSw // this->LedSw->Location = System::Drawing::Point(35, 97); this->LedSw->Name = L"LedSw"; this->LedSw->Size = System::Drawing::Size(75, 59); this->LedSw->TabIndex = 1; this->LedSw->Text = L"LED0"; this->LedSw->UseVisualStyleBackColor = true; this->LedSw->Click += gcnew System::EventHandler(this, &Form1::LedSw_Click); // // textBox1 // this->textBox1->Location = System::Drawing::Point(189, 35); this->textBox1->Name = L"textBox1"; this->textBox1->Size = System::Drawing::Size(197, 22); this->textBox1->TabIndex = 2; // // LedSw1 // this->LedSw1->Location = System::Drawing::Point(147, 97); this->LedSw1->Name = L"LedSw1"; this->LedSw1->Size = System::Drawing::Size(75, 59); this->LedSw1->TabIndex = 3; this->LedSw1->Text = L"LED1"; this->LedSw1->UseVisualStyleBackColor = true; this->LedSw1->Click += gcnew System::EventHandler(this, &Form1::LedSw1_Click); // // LedSw2 // this->LedSw2->Location = System::Drawing::Point(263, 97); this->LedSw2->Name = L"LedSw2"; this->LedSw2->Size = System::Drawing::Size(75, 59); this->LedSw2->TabIndex = 4; this->LedSw2->Text = L"LED2"; this->LedSw2->UseVisualStyleBackColor = true; this->LedSw2->Click += gcnew System::EventHandler(this, &Form1::LedSw2_Click); // // LedSw3 // this->LedSw3->Location = System::Drawing::Point(379, 97); this->LedSw3->Name = L"LedSw3"; this->LedSw3->Size = System::Drawing::Size(75, 59); this->LedSw3->TabIndex = 5; this->LedSw3->Text = L"LED3"; this->LedSw3->UseVisualStyleBackColor = true; this->LedSw3->Click += gcnew System::EventHandler(this, &Form1::LedSw3_Click); // // Form1 // this->AutoScaleDimensions = System::Drawing::SizeF(8, 15); this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font; this->ClientSize = System::Drawing::Size(498, 214); this->Controls->Add(this->LedSw3); this->Controls->Add(this->LedSw2); this->Controls->Add(this->LedSw1); this->Controls->Add(this->textBox1); this->Controls->Add(this->LedSw); this->Controls->Add(this->ConnectBtn); this->Name = L"Form1"; this->Text = L"Form1"; this->ResumeLayout(false); this->PerformLayout(); } #pragma endregion private: System::Void ConnectBtn_Click(System::Object^ sender, System::EventArgs^ e) //USBパイプ接続 { //-------------------------------------------------------BEGIN CUT AND PASTE BLOCK----------------------------------------------------------------------------------- /* Before we can "connect" our application to our USB embedded device, we must first find the device. A USB bus can have many devices simultaneously connected, so somehow we have to find our device, and only our device. This is done with the Vendor ID (VID) and Product ID (PID). Each USB product line should have a unique combination of VID and PID. Microsoft has created a number of functions which are useful for finding plug and play devices. Documentation for each function used can be found in the MSDN library. We will be using the following functions: SetupDiGetClassDevs() //provided by setupapi.dll, which comes with Windows SetupDiEnumDeviceInterfaces() //provided by setupapi.dll, which comes with Windows GetLastError() //provided by kernel32.dll, which comes with Windows SetupDiDestroyDeviceInfoList() //provided by setupapi.dll, which comes with Windows SetupDiGetDeviceInterfaceDetail() //provided by setupapi.dll, which comes with Windows SetupDiGetDeviceRegistryProperty() //provided by setupapi.dll, which comes with Windows malloc() //part of C runtime library, msvcrt.dll? CreateFile() //provided by kernel32.dll, which comes with Windows We will also be using the following unusual data types and structures. Documentation can also be found in the MSDN library: PSP_DEVICE_INTERFACE_DATA PSP_DEVICE_INTERFACE_DETAIL_DATA SP_DEVINFO_DATA HDEVINFO HANDLE GUID The ultimate objective of the following code is to call CreateFile(), which opens a communications pipe to a specific device (such as a HID class USB device endpoint). CreateFile() returns a "handle" which is needed later when calling ReadFile() or WriteFile(). These functions are used to actually send and receive application related data to/from the USB peripheral device. However, in order to call CreateFile(), we first need to get the device path for the USB device with the correct VID and PID. Getting the device path is a multi-step round about process, which requires calling several of the SetupDixxx() functions provided by setupapi.dll. */ //Globally Unique Identifier (GUID) for HID class devices. Windows uses GUIDs to identify things. GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, 0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30}; HDEVINFO DeviceInfoTable = INVALID_HANDLE_VALUE; PSP_DEVICE_INTERFACE_DATA InterfaceDataStructure = new SP_DEVICE_INTERFACE_DATA; PSP_DEVICE_INTERFACE_DETAIL_DATA DetailedInterfaceDataStructure = new SP_DEVICE_INTERFACE_DETAIL_DATA; SP_DEVINFO_DATA DevInfoData; DWORD InterfaceIndex = 0; DWORD StatusLastError = 0; DWORD dwRegType; DWORD dwRegSize; DWORD StructureSize = 0; PBYTE PropertyValueBuffer; bool MatchFound = false; DWORD ErrorStatus; String^ DeviceIDToFind = MY_DEVICE_ID; //First populate a list of plugged in devices (by specifying "DIGCF_PRESENT"), which are of the specified class GUID. DeviceInfoTable = SetupDiGetClassDevsUM(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); //Now look through the list we just populated. We are trying to see if any of them match our device. while(true) { InterfaceDataStructure->cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); if(SetupDiEnumDeviceInterfacesUM(DeviceInfoTable, NULL, &InterfaceClassGuid, InterfaceIndex, InterfaceDataStructure)) { ErrorStatus = GetLastError(); if(ERROR_NO_MORE_ITEMS == ErrorStatus) //Did we reach the end of the list of matching devices in the DeviceInfoTable? { //Cound not find the device. Must not have been attached. SetupDiDestroyDeviceInfoListUM(DeviceInfoTable); //Clean up the old structure we no longer need. return; } } else //Else some other kind of unknown error ocurred... { ErrorStatus = GetLastError(); SetupDiDestroyDeviceInfoListUM(DeviceInfoTable); //Clean up the old structure we no longer need. return; } //Now retrieve the hardware ID from the registry. The hardware ID contains the VID and PID, which we will then //check to see if it is the correct device or not. //Initialize an appropriate SP_DEVINFO_DATA structure. We need this structure for SetupDiGetDeviceRegistryProperty(). DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA); SetupDiEnumDeviceInfoUM(DeviceInfoTable, InterfaceIndex, &DevInfoData); //First query for the size of the hardware ID, so we can know how big a buffer to allocate for the data. SetupDiGetDeviceRegistryPropertyUM(DeviceInfoTable, &DevInfoData, SPDRP_HARDWAREID, &dwRegType, NULL, 0, &dwRegSize); //Allocate a buffer for the hardware ID. PropertyValueBuffer = (BYTE *) malloc (dwRegSize); if(PropertyValueBuffer == NULL) //if null, error, couldn't allocate enough memory { //Can't really recover from this situation, just exit instead. SetupDiDestroyDeviceInfoListUM(DeviceInfoTable); //Clean up the old structure we no longer need. return; } //Retrieve the hardware IDs for the current device we are looking at. PropertyValueBuffer gets filled with a //REG_MULTI_SZ (array of null terminated strings). To find a device, we only care about the very first string in the //buffer, which will be the "device ID". The device ID is a string which contains the VID and PID, in the example //format "Vid_04d8&Pid_003f". SetupDiGetDeviceRegistryPropertyUM(DeviceInfoTable, &DevInfoData, SPDRP_HARDWAREID, &dwRegType, PropertyValueBuffer, dwRegSize, NULL); //Now check if the first string in the hardware ID matches the device ID of my USB device. #ifdef UNICODE String^ DeviceIDFromRegistry = gcnew String((wchar_t *)PropertyValueBuffer); #else String^ DeviceIDFromRegistry = gcnew String((char *)PropertyValueBuffer); #endif free(PropertyValueBuffer); //No longer need the PropertyValueBuffer, free the memory to prevent potential memory leaks textBox1->Text = "Searching Device!"; //Convert both strings to lower case. This makes the code more robust/portable accross OS Versions DeviceIDFromRegistry = DeviceIDFromRegistry->ToLowerInvariant(); DeviceIDToFind = DeviceIDToFind->ToLowerInvariant(); //Now check if the hardware ID we are looking at contains the correct VID/PID MatchFound = DeviceIDFromRegistry->Contains(DeviceIDToFind); if(MatchFound == true) { //Device must have been found. Open read and write handles. In order to do this, we will need the actual device path first. //We can get the path by calling SetupDiGetDeviceInterfaceDetail(), however, we have to call this function twice: The first //time to get the size of the required structure/buffer to hold the detailed interface data, then a second time to actually //get the structure (after we have allocated enough memory for the structure.) DetailedInterfaceDataStructure->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); //First call populates "StructureSize" with the correct value SetupDiGetDeviceInterfaceDetailUM(DeviceInfoTable, InterfaceDataStructure, NULL, NULL, &StructureSize, NULL); DetailedInterfaceDataStructure = (PSP_DEVICE_INTERFACE_DETAIL_DATA)(malloc(StructureSize)); //Allocate enough memory if(DetailedInterfaceDataStructure == NULL) //if null, error, couldn't allocate enough memory { //Can't really recover from this situation, just exit instead. SetupDiDestroyDeviceInfoListUM(DeviceInfoTable); //Clean up the old structure we no longer need. return; } DetailedInterfaceDataStructure->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); //Now call SetupDiGetDeviceInterfaceDetail() a second time to receive the goods. SetupDiGetDeviceInterfaceDetailUM(DeviceInfoTable, InterfaceDataStructure, DetailedInterfaceDataStructure, StructureSize, NULL, NULL); //We now have the proper device path, and we can finally open read and write handles to the device. //We store the handles in the global variables "WriteHandle" and "ReadHandle", which we will use later to actually communicate. WriteHandle = CreateFile((DetailedInterfaceDataStructure->DevicePath), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); ErrorStatus = GetLastError(); if(ErrorStatus == ERROR_SUCCESS) textBox1->Text = "Write Enabled."; //Make button no longer greyed out ReadHandle = CreateFile((DetailedInterfaceDataStructure->DevicePath), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); ErrorStatus = GetLastError(); if(ErrorStatus == ERROR_SUCCESS) { textBox1->Text = "Read Enabled."; //Make label no longer greyed out } SetupDiDestroyDeviceInfoListUM(DeviceInfoTable); //Clean up the old structure we no longer need. //追加修正部 // 接続確認メッセージ準備 OutputPacketBuffer[0] = 0; // Report ID OutputPacketBuffer[1] = 0x30; // 接続確認 // 接続確認 コマンド送信 WriteFile(WriteHandle, &OutputPacketBuffer, 65, &BytesWritten, 0); // OK受信 ReadFile(ReadHandle, &InputPacketBuffer, 65, &BytesRead, 0); // OK確認 if((InputPacketBuffer[3] == 0x4F)&&(InputPacketBuffer[4] == 0x4B)) textBox1->Text = "接続完了!!"; // 接続正常完了 // 接続完了でタイマ1スタート // timer1->Interval = 1000; // timer1->Enabled = TRUE; return; } InterfaceIndex++; //Keep looping until we either find a device with matching VID and PID, or until we run out of items. }//end of while(true) //-------------------------------------------------------END CUT AND PASTE BLOCK------------------------------------------------------------------------------------- } private: System::Void LedSw_Click(System::Object^ sender, System::EventArgs^ e) { OutputPacketBuffer[0] = 0; // Report ID OutputPacketBuffer[1] = 0x31; // LED制御コマンド OutputPacketBuffer[2] = 0x31; // LED0アドレス if(LedMode == 0) //消灯していたなら { LedMode = 1; OutputPacketBuffer[3] = 0x31; // Led点灯 //0x31 = '1' } else { LedMode = 0; OutputPacketBuffer[3] = 0x30; // Led消灯 //0x30 = '0' } // 送信 WriteFile(WriteHandle, &OutputPacketBuffer, 65, &BytesWritten, 0); // 受信 ReadFile(ReadHandle, &InputPacketBuffer, 65, &BytesRead, 0); if(InputPacketBuffer[3] == 0x31)LedSw->BackColor =Color:: Red; // オンなら赤 else LedSw->BackColor = Color::Gray; // オフなら緑 } private: System::Void LedSw1_Click(System::Object^ sender, System::EventArgs^ e) { OutputPacketBuffer[0] = 0; // Report ID OutputPacketBuffer[1] = 0x31; // LED制御コマンド OutputPacketBuffer[2] = 0x32; // LED1アドレス if(Led1Mode == 0) //消灯していたなら { Led1Mode = 1; OutputPacketBuffer[3] = 0x31; // Led点灯 } else { Led1Mode = 0; OutputPacketBuffer[3] = 0x30; // Led消灯 } // 送信 WriteFile(WriteHandle, &OutputPacketBuffer, 65, &BytesWritten, 0); // 受信 ReadFile(ReadHandle, &InputPacketBuffer, 65, &BytesRead, 0); if(InputPacketBuffer[3] == 0x31)LedSw1->BackColor =Color:: Red; // オンなら赤 else LedSw1->BackColor = Color::Gray; // オフなら緑 } private: System::Void LedSw2_Click(System::Object^ sender, System::EventArgs^ e) { OutputPacketBuffer[0] = 0; // Report ID OutputPacketBuffer[1] = 0x31; // LED制御コマンド OutputPacketBuffer[2] = 0x33; // LED2アドレス if(Led2Mode == 0) //消灯していたなら { Led2Mode = 1; OutputPacketBuffer[3] = 0x31; // Led点灯 } else { Led2Mode = 0; OutputPacketBuffer[3] = 0x30; // Led消灯 } // 送信 WriteFile(WriteHandle, &OutputPacketBuffer, 65, &BytesWritten, 0); // 受信 ReadFile(ReadHandle, &InputPacketBuffer, 65, &BytesRead, 0); if(InputPacketBuffer[3] == 0x31)LedSw2->BackColor =Color:: Red; // オンなら赤 else LedSw2->BackColor = Color::Gray; // オフなら緑 } private: System::Void LedSw3_Click(System::Object^ sender, System::EventArgs^ e) { OutputPacketBuffer[0] = 0; // Report ID OutputPacketBuffer[1] = 0x31; // LED制御コマンド OutputPacketBuffer[2] = 0x34; // LED2アドレス if(Led3Mode == 0) //消灯していたなら { Led3Mode = 1; OutputPacketBuffer[3] = 0x31; // Led点灯 } else { Led3Mode = 0; OutputPacketBuffer[3] = 0x30; // Led消灯 } // 送信 WriteFile(WriteHandle, &OutputPacketBuffer, 65, &BytesWritten, 0); // 受信 ReadFile(ReadHandle, &InputPacketBuffer, 65, &BytesRead, 0); if(InputPacketBuffer[3] == 0x31)LedSw3->BackColor =Color:: Red; // オンなら赤 else LedSw3->BackColor = Color::Gray; // オフなら緑 } };//end of class Form1 } |
<実行結果>
接続ボタンをクリックした後、LED0、LED2、LED3を点灯させたところです。
尚、PIC18F14K50のキバン上には本テーマと関係ない部品が多々搭載されています。
■ PIC18F14K50 HIDクラス スイッチの状態 & 電圧読込
<試作品仕様>
・PIC18F14K50のキバン上のスイッチ×3個を押すと PC(Windows)上の対応するovalShape×3個の色が 灰色→オレンジ色に変わること
・PCとPIC間のUSB通信はHIDクラスとする。
・可変抵抗器×3個により設定される電圧をPIC18F14K50の内蔵ADコンバータで読込、この電圧値[V]をPC上の対応するテキストボックス×3個に表示のこと。
・テキストボックスに表示される電圧[V]は、小数点以下2桁とする。
・USB通信ONのボタンボタンスイッチをフォーム上にもうけること。 またUSB通信が確立したら”接続完了!”の文字をテキストボックスに表示のこと
★ ハード 及びPIC側のソフトは こちらを参照願います
<プログラム例> #pragma once //Includes #include <Windows.h> //Definitions for various common and not so common types like DWORD, PCHAR, HANDLE, etc. #include <setupapi.h> //From Platform SDK. Definitions needed for the SetupDixxx() functions, which we use to //find our plug and play device. //Modify this value to match the VID and PID in your USB device descriptor. //Use the formatting: "Vid_xxxx&Pid_xxxx" where xxxx is a 16-bit hexadecimal number. #define MY_DEVICE_ID "Vid_04d8&Pid_003F" namespace CWhiteForm { using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; //-------------------------------------------------------BEGIN CUT AND PASTE BLOCK----------------------------------------------------------------------------------- using namespace System::Runtime::InteropServices; //Need this to support "unmanaged" code. /* In order to use these unmanaged functions from within the managed .NET environment, we need to explicitly import the functions which we will be using from other .DLL file(s). Simply including the appropriate header files is not enough. Note: In order to avoid potential name conflicts in the header files (which we still use), I have renamed the functions by adding "UM" (unmanaged) onto the end of them. To find documentation for the functions in MSDN, search for the function name without the extra "UM" attached. Note2: In the header files (such as setupapi.h), normally the function names are remapped, depending upon if UNICODE is defined or not. For example, two versions of the function SetupDiGetDeviceInterfaceDetail() exist. One for UNICODE, and one for ANSI. If the wrong version of the function is called, things won't work correctly. Therefore, in order to make sure the correct one gets called (based on your compiler settings, which may or may not define "UNICODE"), it is useful to explicity specify the CharSet when doing the DLL import. */ #ifdef UNICODE #define Seeifdef Unicode #else #define Seeifdef Ansi #endif //Returns a HDEVINFO type for a device information set (USB HID devices in //our case). We will need the HDEVINFO as in input parameter for calling many of //the other SetupDixxx() functions. [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiGetClassDevs", CallingConvention=CallingConvention::Winapi)] extern "C" HDEVINFO SetupDiGetClassDevsUM( LPGUID ClassGuid, //Input: Supply the class GUID here. PCTSTR Enumerator, //Input: Use NULL here, not important for our purposes HWND hwndParent, //Input: Use NULL here, not important for our purposes DWORD Flags); //Input: Flags describing what kind of filtering to use. //Gives us "PSP_DEVICE_INTERFACE_DATA" which contains the Interface specific GUID (different //from class GUID). We need the interface GUID to get the device path. [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiEnumDeviceInterfaces", CallingConvention=CallingConvention::Winapi)] extern "C" WINSETUPAPI BOOL WINAPI SetupDiEnumDeviceInterfacesUM( HDEVINFO DeviceInfoSet, //Input: Give it the HDEVINFO we got from SetupDiGetClassDevs() PSP_DEVINFO_DATA DeviceInfoData, //Input (optional) LPGUID InterfaceClassGuid, //Input DWORD MemberIndex, //Input: "Index" of the device you are interested in getting the path for. PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData);//Output: This function fills in an "SP_DEVICE_INTERFACE_DATA" structure. //SetupDiDestroyDeviceInfoList() frees up memory by destroying a DeviceInfoList [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiDestroyDeviceInfoList", CallingConvention=CallingConvention::Winapi)] extern "C" WINSETUPAPI BOOL WINAPI SetupDiDestroyDeviceInfoListUM( HDEVINFO DeviceInfoSet); //Input: Give it a handle to a device info list to deallocate from RAM. //SetupDiEnumDeviceInfo() fills in an "SP_DEVINFO_DATA" structure, which we need for SetupDiGetDeviceRegistryProperty() [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiEnumDeviceInfo", CallingConvention=CallingConvention::Winapi)] extern "C" WINSETUPAPI BOOL WINAPI SetupDiEnumDeviceInfoUM( HDEVINFO DeviceInfoSet, DWORD MemberIndex, PSP_DEVINFO_DATA DeviceInfoData); //SetupDiGetDeviceRegistryProperty() gives us the hardware ID, which we use to check to see if it has matching VID/PID [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiGetDeviceRegistryProperty", CallingConvention=CallingConvention::Winapi)] extern "C" WINSETUPAPI BOOL WINAPI SetupDiGetDeviceRegistryPropertyUM( HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, DWORD Property, PDWORD PropertyRegDataType, PBYTE PropertyBuffer, DWORD PropertyBufferSize, PDWORD RequiredSize); //SetupDiGetDeviceInterfaceDetail() gives us a device path, which is needed before CreateFile() can be used. [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiGetDeviceInterfaceDetail", CallingConvention=CallingConvention::Winapi)] extern "C" BOOL SetupDiGetDeviceInterfaceDetailUM( HDEVINFO DeviceInfoSet, //Input: Wants HDEVINFO which can be obtained from SetupDiGetClassDevs() PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, //Input: Pointer to an structure which defines the device interface. PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData, //Output: Pointer to a strucutre, which will contain the device path. DWORD DeviceInterfaceDetailDataSize, //Input: Number of bytes to retrieve. PDWORD RequiredSize, //Output (optional): Te number of bytes needed to hold the entire struct PSP_DEVINFO_DATA DeviceInfoData); //Output // Variables that need to have wide scope. HANDLE WriteHandle = INVALID_HANDLE_VALUE; //Need to get a write "handle" to our device before we can write to it. HANDLE ReadHandle = INVALID_HANDLE_VALUE; //Need to get a read "handle" to our device before we can read from it. //-------------------------------------------------------END CUT AND PASTE BLOCK------------------------------------------------------------------------------------- //グローバル変数定義 unsigned int ADCValue; DWORD BytesWritten = 0; DWORD BytesRead = 0; unsigned char OutputPacketBuffer[65]; //Allocate a memory buffer equal to our endpoint size + 1 unsigned char InputPacketBuffer[65]; /// <summary> /// Form1 の概要 /// </summary> public ref class Form1 : public System::Windows::Forms::Form { public: Form1(void) { InitializeComponent(); // // ovalShape1->FillStyle = FillStyle.Solid; //サポートされていない //プロパティでFillStyle = Solidを設定する //C#のovalShape1.FillStyle = FillStyle.Solid;はサポートされている ovalShape1->FillColor = Color::Gray; //灰色で塗りつぶし ovalShape2->FillColor = Color::Gray; ovalShape3->FillColor = Color::Gray; } protected: /// <summary> /// 使用中のリソースをすべてクリーンアップします。 /// </summary> ~Form1() { if (components) { delete components; } } private: System::Windows::Forms::Button^ ConnectBtn; protected: protected: private: System::Windows::Forms::Label^ label1; private: System::Windows::Forms::TextBox^ textBox1; private: System::Windows::Forms::TextBox^ textBox2; private: System::Windows::Forms::Timer^ timer1; private: Microsoft::VisualBasic::PowerPacks::ShapeContainer^ shapeContainer1; private: Microsoft::VisualBasic::PowerPacks::OvalShape^ ovalShape3; private: Microsoft::VisualBasic::PowerPacks::OvalShape^ ovalShape2; private: Microsoft::VisualBasic::PowerPacks::OvalShape^ ovalShape1; private: System::Windows::Forms::Label^ label2; private: System::Windows::Forms::Label^ label3; private: System::Windows::Forms::TextBox^ textBox3; private: System::Windows::Forms::TextBox^ textBox4; private: System::Windows::Forms::Label^ label4; private: System::Windows::Forms::Label^ label5; private: System::Windows::Forms::Label^ label6; private: System::ComponentModel::IContainer^ components; private: /// <summary> /// 必要なデザイナー変数です。 /// </summary> #pragma region Windows Form Designer generated code /// <summary> /// デザイナー サポートに必要なメソッドです。このメソッドの内容を /// コード エディターで変更しないでください。 /// </summary> void InitializeComponent(void) { this->components = (gcnew System::ComponentModel::Container()); this->ConnectBtn = (gcnew System::Windows::Forms::Button()); this->label1 = (gcnew System::Windows::Forms::Label()); this->textBox1 = (gcnew System::Windows::Forms::TextBox()); this->textBox2 = (gcnew System::Windows::Forms::TextBox()); this->timer1 = (gcnew System::Windows::Forms::Timer(this->components)); this->shapeContainer1 = (gcnew Microsoft::VisualBasic::PowerPacks::ShapeContainer()); this->ovalShape3 = (gcnew Microsoft::VisualBasic::PowerPacks::OvalShape()); this->ovalShape2 = (gcnew Microsoft::VisualBasic::PowerPacks::OvalShape()); this->ovalShape1 = (gcnew Microsoft::VisualBasic::PowerPacks::OvalShape()); this->label2 = (gcnew System::Windows::Forms::Label()); this->label3 = (gcnew System::Windows::Forms::Label()); this->textBox3 = (gcnew System::Windows::Forms::TextBox()); this->textBox4 = (gcnew System::Windows::Forms::TextBox()); this->label4 = (gcnew System::Windows::Forms::Label()); this->label5 = (gcnew System::Windows::Forms::Label()); this->label6 = (gcnew System::Windows::Forms::Label()); this->SuspendLayout(); // // ConnectBtn // this->ConnectBtn->Location = System::Drawing::Point(24, 27); this->ConnectBtn->Name = L"ConnectBtn"; this->ConnectBtn->Size = System::Drawing::Size(88, 40); this->ConnectBtn->TabIndex = 0; this->ConnectBtn->Text = L"接続"; this->ConnectBtn->UseVisualStyleBackColor = true; this->ConnectBtn->Click += gcnew System::EventHandler(this, &Form1::ConnectBtn_Click); // // label1 // this->label1->AutoSize = true; this->label1->Location = System::Drawing::Point(77, 176); this->label1->Name = L"label1"; this->label1->Size = System::Drawing::Size(35, 15); this->label1->TabIndex = 1; this->label1->Text = L"SW1"; // // textBox1 // this->textBox1->Location = System::Drawing::Point(152, 37); this->textBox1->Name = L"textBox1"; this->textBox1->Size = System::Drawing::Size(100, 22); this->textBox1->TabIndex = 2; // // textBox2 // this->textBox2->Font = (gcnew System::Drawing::Font(L"MS UI Gothic", 13.8F, System::Drawing::FontStyle::Bold, System::Drawing::GraphicsUnit::Point, static_cast<System::Byte>(128))); this->textBox2->Location = System::Drawing::Point(43, 224); this->textBox2->Name = L"textBox2"; this->textBox2->Size = System::Drawing::Size(100, 30); this->textBox2->TabIndex = 3; // // timer1 // this->timer1->Tick += gcnew System::EventHandler(this, &Form1::timer1_Tick); // // shapeContainer1 // this->shapeContainer1->Location = System::Drawing::Point(0, 0); this->shapeContainer1->Margin = System::Windows::Forms::Padding(0); this->shapeContainer1->Name = L"shapeContainer1"; this->shapeContainer1->Shapes->AddRange(gcnew cli::array< Microsoft::VisualBasic::PowerPacks::Shape^ >(3) {this->ovalShape3, this->ovalShape2, this->ovalShape1}); this->shapeContainer1->Size = System::Drawing::Size(526, 313); this->shapeContainer1->TabIndex = 4; this->shapeContainer1->TabStop = false; // // ovalShape3 // this->ovalShape3->FillStyle = Microsoft::VisualBasic::PowerPacks::FillStyle::Solid; this->ovalShape3->Location = System::Drawing::Point(393, 95); this->ovalShape3->Name = L"ovalShape3"; this->ovalShape3->Size = System::Drawing::Size(65, 65); // // ovalShape2 // this->ovalShape2->FillStyle = Microsoft::VisualBasic::PowerPacks::FillStyle::Solid; this->ovalShape2->Location = System::Drawing::Point(235, 97); this->ovalShape2->Name = L"ovalShape2"; this->ovalShape2->Size = System::Drawing::Size(65, 65); // // ovalShape1 // this->ovalShape1->FillStyle = Microsoft::VisualBasic::PowerPacks::FillStyle::Solid; this->ovalShape1->Location = System::Drawing::Point(55, 94); this->ovalShape1->Name = L"ovalShape1"; this->ovalShape1->Size = System::Drawing::Size(65, 65); // // label2 // this->label2->AutoSize = true; this->label2->Location = System::Drawing::Point(256, 176); this->label2->Name = L"label2"; this->label2->Size = System::Drawing::Size(35, 15); this->label2->TabIndex = 5; this->label2->Text = L"SW2"; // // label3 // this->label3->AutoSize = true; this->label3->Location = System::Drawing::Point(408, 176); this->label3->Name = L"label3"; this->label3->Size = System::Drawing::Size(35, 15); this->label3->TabIndex = 6; this->label3->Text = L"SW3"; // // textBox3 // this->textBox3->Font = (gcnew System::Drawing::Font(L"MS UI Gothic", 13.8F, System::Drawing::FontStyle::Bold, System::Drawing::GraphicsUnit::Point, static_cast<System::Byte>(128))); this->textBox3->Location = System::Drawing::Point(213, 224); this->textBox3->Name = L"textBox3"; this->textBox3->Size = System::Drawing::Size(100, 30); this->textBox3->TabIndex = 7; // // textBox4 // this->textBox4->Font = (gcnew System::Drawing::Font(L"MS UI Gothic", 13.8F, System::Drawing::FontStyle::Bold, System::Drawing::GraphicsUnit::Point, static_cast<System::Byte>(128))); this->textBox4->Location = System::Drawing::Point(387, 224); this->textBox4->Name = L"textBox4"; this->textBox4->Size = System::Drawing::Size(100, 30); this->textBox4->TabIndex = 8; // // label4 // this->label4->AutoSize = true; this->label4->Location = System::Drawing::Point(79, 269); this->label4->Name = L"label4"; this->label4->Size = System::Drawing::Size(33, 15); this->label4->TabIndex = 9; this->label4->Text = L"VR1"; // // label5 // this->label5->AutoSize = true; this->label5->Location = System::Drawing::Point(256, 269); this->label5->Name = L"label5"; this->label5->Size = System::Drawing::Size(33, 15); this->label5->TabIndex = 10; this->label5->Text = L"VR2"; // // label6 // this->label6->AutoSize = true; this->label6->Location = System::Drawing::Point(422, 269); this->label6->Name = L"label6"; this->label6->Size = System::Drawing::Size(33, 15); this->label6->TabIndex = 11; this->label6->Text = L"VR3"; // // Form1 // this->AutoScaleDimensions = System::Drawing::SizeF(8, 15); this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font; this->ClientSize = System::Drawing::Size(526, 313); this->Controls->Add(this->label6); this->Controls->Add(this->label5); this->Controls->Add(this->label4); this->Controls->Add(this->textBox4); this->Controls->Add(this->textBox3); this->Controls->Add(this->label3); this->Controls->Add(this->label2); this->Controls->Add(this->textBox2); this->Controls->Add(this->textBox1); this->Controls->Add(this->label1); this->Controls->Add(this->ConnectBtn); this->Controls->Add(this->shapeContainer1); this->Name = L"Form1"; this->Text = L"Form1"; this->ResumeLayout(false); this->PerformLayout(); } #pragma endregion private: System::Void ConnectBtn_Click(System::Object^ sender, System::EventArgs^ e) { //-------------------------------------------------------BEGIN CUT AND PASTE BLOCK----------------------------------------------------------------------------------- /* Before we can "connect" our application to our USB embedded device, we must first find the device. A USB bus can have many devices simultaneously connected, so somehow we have to find our device, and only our device. This is done with the Vendor ID (VID) and Product ID (PID). Each USB product line should have a unique combination of VID and PID. Microsoft has created a number of functions which are useful for finding plug and play devices. Documentation for each function used can be found in the MSDN library. We will be using the following functions: SetupDiGetClassDevs() //provided by setupapi.dll, which comes with Windows SetupDiEnumDeviceInterfaces() //provided by setupapi.dll, which comes with Windows GetLastError() //provided by kernel32.dll, which comes with Windows SetupDiDestroyDeviceInfoList() //provided by setupapi.dll, which comes with Windows SetupDiGetDeviceInterfaceDetail() //provided by setupapi.dll, which comes with Windows SetupDiGetDeviceRegistryProperty() //provided by setupapi.dll, which comes with Windows malloc() //part of C runtime library, msvcrt.dll? CreateFile() //provided by kernel32.dll, which comes with Windows We will also be using the following unusual data types and structures. Documentation can also be found in the MSDN library: PSP_DEVICE_INTERFACE_DATA PSP_DEVICE_INTERFACE_DETAIL_DATA SP_DEVINFO_DATA HDEVINFO HANDLE GUID The ultimate objective of the following code is to call CreateFile(), which opens a communications pipe to a specific device (such as a HID class USB device endpoint). CreateFile() returns a "handle" which is needed later when calling ReadFile() or WriteFile(). These functions are used to actually send and receive application related data to/from the USB peripheral device. However, in order to call CreateFile(), we first need to get the device path for the USB device with the correct VID and PID. Getting the device path is a multi-step round about process, which requires calling several of the SetupDixxx() functions provided by setupapi.dll. */ //Globally Unique Identifier (GUID) for HID class devices. Windows uses GUIDs to identify things. GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, 0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30}; HDEVINFO DeviceInfoTable = INVALID_HANDLE_VALUE; PSP_DEVICE_INTERFACE_DATA InterfaceDataStructure = new SP_DEVICE_INTERFACE_DATA; PSP_DEVICE_INTERFACE_DETAIL_DATA DetailedInterfaceDataStructure = new SP_DEVICE_INTERFACE_DETAIL_DATA; SP_DEVINFO_DATA DevInfoData; DWORD InterfaceIndex = 0; DWORD StatusLastError = 0; DWORD dwRegType; DWORD dwRegSize; DWORD StructureSize = 0; PBYTE PropertyValueBuffer; bool MatchFound = false; DWORD ErrorStatus; String^ DeviceIDToFind = MY_DEVICE_ID; //First populate a list of plugged in devices (by specifying "DIGCF_PRESENT"), which are of the specified class GUID. DeviceInfoTable = SetupDiGetClassDevsUM(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); //Now look through the list we just populated. We are trying to see if any of them match our device. while(true) { InterfaceDataStructure->cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); if(SetupDiEnumDeviceInterfacesUM(DeviceInfoTable, NULL, &InterfaceClassGuid, InterfaceIndex, InterfaceDataStructure)) { ErrorStatus = GetLastError(); if(ERROR_NO_MORE_ITEMS == ErrorStatus) //Did we reach the end of the list of matching devices in the DeviceInfoTable? { //Cound not find the device. Must not have been attached. SetupDiDestroyDeviceInfoListUM(DeviceInfoTable); //Clean up the old structure we no longer need. return; } } else //Else some other kind of unknown error ocurred... { ErrorStatus = GetLastError(); SetupDiDestroyDeviceInfoListUM(DeviceInfoTable); //Clean up the old structure we no longer need. return; } //Now retrieve the hardware ID from the registry. The hardware ID contains the VID and PID, which we will then //check to see if it is the correct device or not. //Initialize an appropriate SP_DEVINFO_DATA structure. We need this structure for SetupDiGetDeviceRegistryProperty(). DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA); SetupDiEnumDeviceInfoUM(DeviceInfoTable, InterfaceIndex, &DevInfoData); //First query for the size of the hardware ID, so we can know how big a buffer to allocate for the data. SetupDiGetDeviceRegistryPropertyUM(DeviceInfoTable, &DevInfoData, SPDRP_HARDWAREID, &dwRegType, NULL, 0, &dwRegSize); //Allocate a buffer for the hardware ID. PropertyValueBuffer = (BYTE *) malloc (dwRegSize); if(PropertyValueBuffer == NULL) //if null, error, couldn't allocate enough memory { //Can't really recover from this situation, just exit instead. SetupDiDestroyDeviceInfoListUM(DeviceInfoTable); //Clean up the old structure we no longer need. return; } //Retrieve the hardware IDs for the current device we are looking at. PropertyValueBuffer gets filled with a //REG_MULTI_SZ (array of null terminated strings). To find a device, we only care about the very first string in the //buffer, which will be the "device ID". The device ID is a string which contains the VID and PID, in the example //format "Vid_04d8&Pid_003f". SetupDiGetDeviceRegistryPropertyUM(DeviceInfoTable, &DevInfoData, SPDRP_HARDWAREID, &dwRegType, PropertyValueBuffer, dwRegSize, NULL); //Now check if the first string in the hardware ID matches the device ID of my USB device. #ifdef UNICODE String^ DeviceIDFromRegistry = gcnew String((wchar_t *)PropertyValueBuffer); #else String^ DeviceIDFromRegistry = gcnew String((char *)PropertyValueBuffer); #endif free(PropertyValueBuffer); //No longer need the PropertyValueBuffer, free the memory to prevent potential memory leaks textBox1->Text = "Searching Device!"; //Convert both strings to lower case. This makes the code more robust/portable accross OS Versions DeviceIDFromRegistry = DeviceIDFromRegistry->ToLowerInvariant(); DeviceIDToFind = DeviceIDToFind->ToLowerInvariant(); //Now check if the hardware ID we are looking at contains the correct VID/PID MatchFound = DeviceIDFromRegistry->Contains(DeviceIDToFind); if(MatchFound == true) { //Device must have been found. Open read and write handles. In order to do this, we will need the actual device path first. //We can get the path by calling SetupDiGetDeviceInterfaceDetail(), however, we have to call this function twice: The first //time to get the size of the required structure/buffer to hold the detailed interface data, then a second time to actually //get the structure (after we have allocated enough memory for the structure.) DetailedInterfaceDataStructure->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); //First call populates "StructureSize" with the correct value SetupDiGetDeviceInterfaceDetailUM(DeviceInfoTable, InterfaceDataStructure, NULL, NULL, &StructureSize, NULL); DetailedInterfaceDataStructure = (PSP_DEVICE_INTERFACE_DETAIL_DATA)(malloc(StructureSize)); //Allocate enough memory if(DetailedInterfaceDataStructure == NULL) //if null, error, couldn't allocate enough memory { //Can't really recover from this situation, just exit instead. SetupDiDestroyDeviceInfoListUM(DeviceInfoTable); //Clean up the old structure we no longer need. return; } DetailedInterfaceDataStructure->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); //Now call SetupDiGetDeviceInterfaceDetail() a second time to receive the goods. SetupDiGetDeviceInterfaceDetailUM(DeviceInfoTable, InterfaceDataStructure, DetailedInterfaceDataStructure, StructureSize, NULL, NULL); //We now have the proper device path, and we can finally open read and write handles to the device. //We store the handles in the global variables "WriteHandle" and "ReadHandle", which we will use later to actually communicate. WriteHandle = CreateFile((DetailedInterfaceDataStructure->DevicePath), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); ErrorStatus = GetLastError(); if(ErrorStatus == ERROR_SUCCESS) textBox1->Text = "Write Enabled."; //Make button no longer greyed out ReadHandle = CreateFile((DetailedInterfaceDataStructure->DevicePath), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); ErrorStatus = GetLastError(); if(ErrorStatus == ERROR_SUCCESS) { textBox1->Text = "Read Enabled."; //Make label no longer greyed out } SetupDiDestroyDeviceInfoListUM(DeviceInfoTable); //Clean up the old structure we no longer need. //追加修正部 OutputPacketBuffer[0] = 0; // Report ID OutputPacketBuffer[1] = 0x30; // 接続確認 WriteFile(WriteHandle, &OutputPacketBuffer, 65, &BytesWritten, 0); ReadFile(ReadHandle, &InputPacketBuffer, 65, &BytesRead, 0); if((InputPacketBuffer[3] == 0x4F)&&(InputPacketBuffer[4] == 0x4B)) textBox1->Text = "接続完了!"; // 接続正常完了 //タイマ1スタート timer1->Interval = 100; //インターバル時間:100msec timer1->Enabled = TRUE; return; } InterfaceIndex++; //Keep looping until we either find a device with matching VID and PID, or until we run out of items. }//end of while(true) //-------------------------------------------------------END CUT AND PASTE BLOCK------------------------------------------------------------------------------------- } private: System::Void timer1_Tick(System::Object^ sender, System::EventArgs^ e) //インターバルタイマ //100msec { unsigned int ADCValue; double Volt; //可変抵抗器出力電圧のAD変換値 読込 OutputPacketBuffer[0] = 0; // Report ID OutputPacketBuffer[1] = 0x33; // 可変抵抗器outのAD変換値読込 OutputPacketBuffer[2] = 0x31; // VR1 WriteFile(WriteHandle, &OutputPacketBuffer, 65, &BytesWritten, 0); ReadFile(ReadHandle, &InputPacketBuffer, 65, &BytesRead, 0); ADCValue = (InputPacketBuffer[4] * 256) + InputPacketBuffer[3]; Volt = (ADCValue * 5.0) / 1024; textBox2->Text = String::Format("{0:F2} [V]",Volt); //小数点以下2桁にして表示 OutputPacketBuffer[2] = 0x32; // VR2 WriteFile(WriteHandle, &OutputPacketBuffer, 65, &BytesWritten, 0); ReadFile(ReadHandle, &InputPacketBuffer, 65, &BytesRead, 0); ADCValue = (InputPacketBuffer[4] * 256) + InputPacketBuffer[3]; Volt = (ADCValue * 5.0) / 1024; textBox3->Text = String::Format("{0:F2} [V]",Volt); //小数点以下2桁にして表示 OutputPacketBuffer[2] = 0x33; // VR3 WriteFile(WriteHandle, &OutputPacketBuffer, 65, &BytesWritten, 0); ReadFile(ReadHandle, &InputPacketBuffer, 65, &BytesRead, 0); ADCValue = (InputPacketBuffer[4] * 256) + InputPacketBuffer[3]; Volt = (ADCValue * 5.0) / 1024; textBox4->Text = String::Format("{0:F2} [V]",Volt); //小数点以下2桁にして表示 //スイッチの状態読込 OutputPacketBuffer[0] = 0; // Report ID OutputPacketBuffer[1] = 0x32; // SW状態要求コマンド OutputPacketBuffer[2] = 0x31; // SW1 WriteFile(WriteHandle, &OutputPacketBuffer, 65, &BytesWritten, 0); ReadFile(ReadHandle, &InputPacketBuffer, 65, &BytesRead, 0); if(InputPacketBuffer[3] == 0x30) // オンか? ovalShape1->FillColor = Color::Orange; //ON ならオレンジ色 else ovalShape1->FillColor = Color::Gray; // OFFなら灰色 OutputPacketBuffer[2] = 0x32; // SW2 WriteFile(WriteHandle, &OutputPacketBuffer, 65, &BytesWritten, 0); ReadFile(ReadHandle, &InputPacketBuffer, 65, &BytesRead, 0); if(InputPacketBuffer[3] == 0x30) // オンか? ovalShape2->FillColor = Color::Orange; //ON ならオレンジ色 else ovalShape2->FillColor = Color::Gray; // OFFなら灰色 OutputPacketBuffer[2] = 0x33; // SW3 WriteFile(WriteHandle, &OutputPacketBuffer, 65, &BytesWritten, 0); ReadFile(ReadHandle, &InputPacketBuffer, 65, &BytesRead, 0); if(InputPacketBuffer[3] == 0x30) // オンか? ovalShape3->FillColor = Color::Orange; //ON ならオレンジ色 else ovalShape3->FillColor = Color::Gray; // OFFなら灰色 } }; }
|
<実行結果>
・接続ボタンをクリックしてUSB接続をした後に スイッチボタンSW1とSW3を押した時のPCのフォーム画面です。
・ovalShapeがオレンジ色になっているスイッチはON状態をあらわし、灰色はOFF状態の場合です。
・テキストボックスに表示されている値は、PIC18F14K50が読み込んだ電圧値で、小数点以下2桁で表示されています。
■ PIC32MX795F512L HIDクラス LED点灯/消灯、ADコンバータ読込・表示
<試作品仕様>
・PCとPIC間のUSB通信はHIDクラスとする。
・PC(Windows)上のボタンスイッチ×4個をクリックすると PIC32MX795F512Lのキバン上の対応するLEDがON/OFFすること
・PCとPIC間のUSB通信はHIDクラスとする。
・ボタンスイッチはLEDがONしているときは赤色、OFFの時は灰色とする。
・USB通信ONのボタンボタンスイッチをフォーム上にもうけること。 またUSB通信が確立したら”接続完了!”の文字をテキストボックスに表示のこと
・PIC32MX795F512Lのキバン上のスイッチ×3個を押すと PC(Windows)上の対応するovalShape×3個の色が 灰色→オレンジ色に変わること
・可変抵抗器により設定される電圧をPIC32MX795F512L内蔵ADコンバータで読込、この電圧値[V]をPC上のテキストボックスに表示のこと。
・テキストボックスに表示される電圧[V]は、小数点以下2桁とする。
・USB通信ONのボタンボタンスイッチをフォーム上にもうけること。 またUSB通信が確立したら”接続完了!”の文字をテキストボックスに表示のこと
★ PIC側のハードとソフトは こちらを参照願います
<プログラム例> //Form1.h #pragma once //Includes #include <Windows.h> //Definitions for various common and not so common types like DWORD, PCHAR, HANDLE, etc. #include <setupapi.h> //From Platform SDK. Definitions needed for the SetupDixxx() functions, which we use to //find our plug and play device. //Modify this value to match the VID and PID in your USB device descriptor. //Use the formatting: "Vid_xxxx&Pid_xxxx" where xxxx is a 16-bit hexadecimal number. #define MY_DEVICE_ID "Vid_04d8&Pid_003F" //VID = 0x04D8 PID = 0x003F namespace CWhiteForm { using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; //-------------------------------------------------------BEGIN CUT AND PASTE BLOCK----------------------------------------------------------------------------------- using namespace System::Runtime::InteropServices; //Need this to support "unmanaged" code. /* In order to use these unmanaged functions from within the managed .NET environment, we need to explicitly import the functions which we will be using from other .DLL file(s). Simply including the appropriate header files is not enough. Note: In order to avoid potential name conflicts in the header files (which we still use), I have renamed the functions by adding "UM" (unmanaged) onto the end of them. To find documentation for the functions in MSDN, search for the function name without the extra "UM" attached. Note2: In the header files (such as setupapi.h), normally the function names are remapped, depending upon if UNICODE is defined or not. For example, two versions of the function SetupDiGetDeviceInterfaceDetail() exist. One for UNICODE, and one for ANSI. If the wrong version of the function is called, things won't work correctly. Therefore, in order to make sure the correct one gets called (based on your compiler settings, which may or may not define "UNICODE"), it is useful to explicity specify the CharSet when doing the DLL import. */ #ifdef UNICODE #define Seeifdef Unicode #else #define Seeifdef Ansi #endif //使用するDLL関数の宣言 //Returns a HDEVINFO type for a device information set (USB HID devices in //our case). We will need the HDEVINFO as in input parameter for calling many of //the other SetupDixxx() functions. [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiGetClassDevs", CallingConvention=CallingConvention::Winapi)] extern "C" HDEVINFO SetupDiGetClassDevsUM( LPGUID ClassGuid, //Input: Supply the class GUID here. PCTSTR Enumerator, //Input: Use NULL here, not important for our purposes HWND hwndParent, //Input: Use NULL here, not important for our purposes DWORD Flags); //Input: Flags describing what kind of filtering to use. //Gives us "PSP_DEVICE_INTERFACE_DATA" which contains the Interface specific GUID (different //from class GUID). We need the interface GUID to get the device path. [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiEnumDeviceInterfaces", CallingConvention=CallingConvention::Winapi)] extern "C" WINSETUPAPI BOOL WINAPI SetupDiEnumDeviceInterfacesUM( HDEVINFO DeviceInfoSet, //Input: Give it the HDEVINFO we got from SetupDiGetClassDevs() PSP_DEVINFO_DATA DeviceInfoData, //Input (optional) LPGUID InterfaceClassGuid, //Input DWORD MemberIndex, //Input: "Index" of the device you are interested in getting the path for. PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData);//Output: This function fills in an "SP_DEVICE_INTERFACE_DATA" structure. //SetupDiDestroyDeviceInfoList() frees up memory by destroying a DeviceInfoList [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiDestroyDeviceInfoList", CallingConvention=CallingConvention::Winapi)] extern "C" WINSETUPAPI BOOL WINAPI SetupDiDestroyDeviceInfoListUM( HDEVINFO DeviceInfoSet); //Input: Give it a handle to a device info list to deallocate from RAM. //SetupDiEnumDeviceInfo() fills in an "SP_DEVINFO_DATA" structure, which we need for SetupDiGetDeviceRegistryProperty() [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiEnumDeviceInfo", CallingConvention=CallingConvention::Winapi)] extern "C" WINSETUPAPI BOOL WINAPI SetupDiEnumDeviceInfoUM( HDEVINFO DeviceInfoSet, DWORD MemberIndex, PSP_DEVINFO_DATA DeviceInfoData); //SetupDiGetDeviceRegistryProperty() gives us the hardware ID, which we use to check to see if it has matching VID/PID [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiGetDeviceRegistryProperty", CallingConvention=CallingConvention::Winapi)] extern "C" WINSETUPAPI BOOL WINAPI SetupDiGetDeviceRegistryPropertyUM( HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, DWORD Property, PDWORD PropertyRegDataType, PBYTE PropertyBuffer, DWORD PropertyBufferSize, PDWORD RequiredSize); //SetupDiGetDeviceInterfaceDetail() gives us a device path, which is needed before CreateFile() can be used. [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiGetDeviceInterfaceDetail", CallingConvention=CallingConvention::Winapi)] extern "C" BOOL SetupDiGetDeviceInterfaceDetailUM( HDEVINFO DeviceInfoSet, //Input: Wants HDEVINFO which can be obtained from SetupDiGetClassDevs() PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, //Input: Pointer to an structure which defines the device interface. PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData, //Output: Pointer to a strucutre, which will contain the device path. DWORD DeviceInterfaceDetailDataSize, //Input: Number of bytes to retrieve. PDWORD RequiredSize, //Output (optional): Te number of bytes needed to hold the entire struct PSP_DEVINFO_DATA DeviceInfoData); //Output // Variables that need to have wide scope. HANDLE WriteHandle = INVALID_HANDLE_VALUE; //Need to get a write "handle" to our device before we can write to it. HANDLE ReadHandle = INVALID_HANDLE_VALUE; //Need to get a read "handle" to our device before we can read from it. //-------------------------------------------------------END CUT AND PASTE BLOCK------------------------------------------------------------------------------------- //グローバル変数定義 int Led0Mode = 0, // LedMode = 0; 消灯 1:点灯 Led1Mode = 0, // Led1Mode = 0; 消灯 1:点灯 Led2Mode = 0, // Led2Mode = 0; 消灯 1:点灯 Led3Mode = 0; // Lee3Mode = 0; 消灯 1:点灯 DWORD BytesWritten = 0; DWORD BytesRead = 0; unsigned char OutputPacketBuffer[65]; //Allocate a memory buffer equal to our endpoint size + 1 unsigned char InputPacketBuffer[65]; /// <summary> /// Form1 の概要 /// </summary> public ref class Form1 : public System::Windows::Forms::Form { public: Form1(void) { InitializeComponent(); // //TODO: ここにコンストラクター コードを追加します // } protected: /// <summary> /// 使用中のリソースをすべてクリーンアップします。 /// </summary> ~Form1() { if (components) { delete components; } } private: System::Windows::Forms::GroupBox^ groupBox1; private: System::Windows::Forms::Button^ Led0Btn; protected: private: System::Windows::Forms::Button^ ConnectBtn; private: System::Windows::Forms::TextBox^ textBox1; private: System::Windows::Forms::Button^ Led1Btn; private: System::Windows::Forms::Button^ Led2Btn; private: System::Windows::Forms::Button^ Led3Btn; private: System::Windows::Forms::GroupBox^ groupBox2; private: System::Windows::Forms::Label^ label3; private: System::Windows::Forms::Label^ label2; private: System::Windows::Forms::Label^ label1; private: Microsoft::VisualBasic::PowerPacks::ShapeContainer^ shapeContainer1; private: Microsoft::VisualBasic::PowerPacks::OvalShape^ ovalShape3; private: Microsoft::VisualBasic::PowerPacks::OvalShape^ ovalShape2; private: Microsoft::VisualBasic::PowerPacks::OvalShape^ ovalShape1; private: System::Windows::Forms::Timer^ timer1; private: System::Windows::Forms::GroupBox^ groupBox3; private: System::Windows::Forms::TextBox^ textBox2; private: System::ComponentModel::IContainer^ components; private: /// <summary> /// 必要なデザイナー変数です。 /// </summary> #pragma region Windows Form Designer generated code /// <summary> /// デザイナー サポートに必要なメソッドです。このメソッドの内容を /// コード エディターで変更しないでください。 /// </summary> void InitializeComponent(void) { this->components = (gcnew System::ComponentModel::Container()); this->groupBox1 = (gcnew System::Windows::Forms::GroupBox()); this->Led3Btn = (gcnew System::Windows::Forms::Button()); this->Led2Btn = (gcnew System::Windows::Forms::Button()); this->Led1Btn = (gcnew System::Windows::Forms::Button()); this->Led0Btn = (gcnew System::Windows::Forms::Button()); this->ConnectBtn = (gcnew System::Windows::Forms::Button()); this->textBox1 = (gcnew System::Windows::Forms::TextBox()); this->groupBox2 = (gcnew System::Windows::Forms::GroupBox()); this->label3 = (gcnew System::Windows::Forms::Label()); this->label2 = (gcnew System::Windows::Forms::Label()); this->label1 = (gcnew System::Windows::Forms::Label()); this->shapeContainer1 = (gcnew Microsoft::VisualBasic::PowerPacks::ShapeContainer()); this->ovalShape3 = (gcnew Microsoft::VisualBasic::PowerPacks::OvalShape()); this->ovalShape2 = (gcnew Microsoft::VisualBasic::PowerPacks::OvalShape()); this->ovalShape1 = (gcnew Microsoft::VisualBasic::PowerPacks::OvalShape()); this->timer1 = (gcnew System::Windows::Forms::Timer(this->components)); this->groupBox3 = (gcnew System::Windows::Forms::GroupBox()); this->textBox2 = (gcnew System::Windows::Forms::TextBox()); this->groupBox1->SuspendLayout(); this->groupBox2->SuspendLayout(); this->groupBox3->SuspendLayout(); this->SuspendLayout(); // // groupBox1 // this->groupBox1->BackColor = System::Drawing::Color::FromArgb(static_cast<System::Int32>(static_cast<System::Byte>(0)), static_cast<System::Int32>(static_cast<System::Byte>(192)), static_cast<System::Int32>(static_cast<System::Byte>(192))); this->groupBox1->Controls->Add(this->Led3Btn); this->groupBox1->Controls->Add(this->Led2Btn); this->groupBox1->Controls->Add(this->Led1Btn); this->groupBox1->Controls->Add(this->Led0Btn); this->groupBox1->Font = (gcnew System::Drawing::Font(L"MS UI Gothic", 10.2F, System::Drawing::FontStyle::Bold, System::Drawing::GraphicsUnit::Point, static_cast<System::Byte>(128))); this->groupBox1->Location = System::Drawing::Point(24, 63); this->groupBox1->Name = L"groupBox1"; this->groupBox1->Size = System::Drawing::Size(501, 122); this->groupBox1->TabIndex = 0; this->groupBox1->TabStop = false; this->groupBox1->Text = L"LED OnOff照光スイッチ"; // // Led3Btn // this->Led3Btn->Location = System::Drawing::Point(347, 44); this->Led3Btn->Name = L"Led3Btn"; this->Led3Btn->Size = System::Drawing::Size(105, 57); this->Led3Btn->TabIndex = 3; this->Led3Btn->Text = L"LED3"; this->Led3Btn->UseVisualStyleBackColor = true; this->Led3Btn->Click += gcnew System::EventHandler(this, &Form1::Led3Btn_Click); // // Led2Btn // this->Led2Btn->Location = System::Drawing::Point(236, 44); this->Led2Btn->Name = L"Led2Btn"; this->Led2Btn->Size = System::Drawing::Size(105, 57); this->Led2Btn->TabIndex = 2; this->Led2Btn->Text = L"LED2"; this->Led2Btn->UseVisualStyleBackColor = true; this->Led2Btn->Click += gcnew System::EventHandler(this, &Form1::Led2Btn_Click); // // Led1Btn // this->Led1Btn->Location = System::Drawing::Point(125, 44); this->Led1Btn->Name = L"Led1Btn"; this->Led1Btn->Size = System::Drawing::Size(105, 57); this->Led1Btn->TabIndex = 1; this->Led1Btn->Text = L"LED1"; this->Led1Btn->UseVisualStyleBackColor = true; this->Led1Btn->Click += gcnew System::EventHandler(this, &Form1::Led1Btn_Click); // // Led0Btn // this->Led0Btn->Location = System::Drawing::Point(15, 44); this->Led0Btn->Name = L"Led0Btn"; this->Led0Btn->Size = System::Drawing::Size(104, 57); this->Led0Btn->TabIndex = 0; this->Led0Btn->Text = L"LED0"; this->Led0Btn->UseVisualStyleBackColor = true; this->Led0Btn->Click += gcnew System::EventHandler(this, &Form1::Led0Btn_Click); // // ConnectBtn // this->ConnectBtn->Location = System::Drawing::Point(24, 12); this->ConnectBtn->Name = L"ConnectBtn"; this->ConnectBtn->Size = System::Drawing::Size(75, 33); this->ConnectBtn->TabIndex = 1; this->ConnectBtn->Text = L"接続"; this->ConnectBtn->UseVisualStyleBackColor = true; this->ConnectBtn->Click += gcnew System::EventHandler(this, &Form1::ConnectBtn_Click); // // textBox1 // this->textBox1->Location = System::Drawing::Point(149, 18); this->textBox1->Name = L"textBox1"; this->textBox1->Size = System::Drawing::Size(161, 22); this->textBox1->TabIndex = 2; // // groupBox2 // this->groupBox2->BackColor = System::Drawing::Color::FromArgb(static_cast<System::Int32>(static_cast<System::Byte>(0)), static_cast<System::Int32>(static_cast<System::Byte>(192)), static_cast<System::Int32>(static_cast<System::Byte>(192))); this->groupBox2->Controls->Add(this->label3); this->groupBox2->Controls->Add(this->label2); this->groupBox2->Controls->Add(this->label1); this->groupBox2->Controls->Add(this->shapeContainer1); this->groupBox2->Location = System::Drawing::Point(24, 204); this->groupBox2->Name = L"groupBox2"; this->groupBox2->Size = System::Drawing::Size(446, 114); this->groupBox2->TabIndex = 3; this->groupBox2->TabStop = false; this->groupBox2->Text = L"スイッチ状態検出"; // // label3 // this->label3->AutoSize = true; this->label3->Location = System::Drawing::Point(307, 90); this->label3->Name = L"label3"; this->label3->Size = System::Drawing::Size(72, 15); this->label3->TabIndex = 3; this->label3->Text = L"SW3(RD7)"; // // label2 // this->label2->AutoSize = true; this->label2->Location = System::Drawing::Point(189, 90); this->label2->Name = L"label2"; this->label2->Size = System::Drawing::Size(72, 15); this->label2->TabIndex = 2; this->label2->Text = L"SW2(RD6)"; // // label1 // this->label1->AutoSize = true; this->label1->Location = System::Drawing::Point(83, 90); this->label1->Name = L"label1"; this->label1->Size = System::Drawing::Size(80, 15); this->label1->TabIndex = 1; this->label1->Text = L"SW1(RD13)"; // // shapeContainer1 // this->shapeContainer1->Location = System::Drawing::Point(3, 18); this->shapeContainer1->Margin = System::Windows::Forms::Padding(0); this->shapeContainer1->Name = L"shapeContainer1"; this->shapeContainer1->Shapes->AddRange(gcnew cli::array< Microsoft::VisualBasic::PowerPacks::Shape^ >(3) {this->ovalShape3, this->ovalShape2, this->ovalShape1}); this->shapeContainer1->Size = System::Drawing::Size(440, 93); this->shapeContainer1->TabIndex = 0; this->shapeContainer1->TabStop = false; // // ovalShape3 // this->ovalShape3->FillStyle = Microsoft::VisualBasic::PowerPacks::FillStyle::Solid; this->ovalShape3->Location = System::Drawing::Point(307, 7); this->ovalShape3->Name = L"ovalShape3"; this->ovalShape3->Size = System::Drawing::Size(50, 50); // // ovalShape2 // this->ovalShape2->FillStyle = Microsoft::VisualBasic::PowerPacks::FillStyle::Solid; this->ovalShape2->Location = System::Drawing::Point(193, 10); this->ovalShape2->Name = L"ovalShape2"; this->ovalShape2->Size = System::Drawing::Size(50, 50); // // ovalShape1 // this->ovalShape1->FillStyle = Microsoft::VisualBasic::PowerPacks::FillStyle::Solid; this->ovalShape1->Location = System::Drawing::Point(81, 10); this->ovalShape1->Name = L"ovalShape1"; this->ovalShape1->Size = System::Drawing::Size(50, 50); // // timer1 // this->timer1->Tick += gcnew System::EventHandler(this, &Form1::timer1_Tick); // // groupBox3 // this->groupBox3->BackColor = System::Drawing::Color::FromArgb(static_cast<System::Int32>(static_cast<System::Byte>(0)), static_cast<System::Int32>(static_cast<System::Byte>(192)), static_cast<System::Int32>(static_cast<System::Byte>(192))); this->groupBox3->Controls->Add(this->textBox2); this->groupBox3->Location = System::Drawing::Point(137, 345); this->groupBox3->Name = L"groupBox3"; this->groupBox3->Size = System::Drawing::Size(228, 71); this->groupBox3->TabIndex = 4; this->groupBox3->TabStop = false; this->groupBox3->Text = L"電圧検出"; // // textBox2 // this->textBox2->Font = (gcnew System::Drawing::Font(L"MS UI Gothic", 13.8F, System::Drawing::FontStyle::Bold, System::Drawing::GraphicsUnit::Point, static_cast<System::Byte>(128))); this->textBox2->Location = System::Drawing::Point(60, 21); this->textBox2->Name = L"textBox2"; this->textBox2->Size = System::Drawing::Size(129, 30); this->textBox2->TabIndex = 0; // // Form1 // this->AutoScaleDimensions = System::Drawing::SizeF(8, 15); this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font; this->ClientSize = System::Drawing::Size(537, 466); this->Controls->Add(this->groupBox3); this->Controls->Add(this->groupBox2); this->Controls->Add(this->textBox1); this->Controls->Add(this->ConnectBtn); this->Controls->Add(this->groupBox1); this->Name = L"Form1"; this->Text = L"Form1"; this->groupBox1->ResumeLayout(false); this->groupBox2->ResumeLayout(false); this->groupBox2->PerformLayout(); this->groupBox3->ResumeLayout(false); this->groupBox3->PerformLayout(); this->ResumeLayout(false); this->PerformLayout(); } #pragma endregion private: System::Void ConnectBtn_Click(System::Object^ sender, System::EventArgs^ e) { //-------------------------------------------------------BEGIN CUT AND PASTE BLOCK----------------------------------------------------------------------------------- /* Before we can "connect" our application to our USB embedded device, we must first find the device. A USB bus can have many devices simultaneously connected, so somehow we have to find our device, and only our device. This is done with the Vendor ID (VID) and Product ID (PID). Each USB product line should have a unique combination of VID and PID. Microsoft has created a number of functions which are useful for finding plug and play devices. Documentation for each function used can be found in the MSDN library. We will be using the following functions: SetupDiGetClassDevs() //provided by setupapi.dll, which comes with Windows SetupDiEnumDeviceInterfaces() //provided by setupapi.dll, which comes with Windows GetLastError() //provided by kernel32.dll, which comes with Windows SetupDiDestroyDeviceInfoList() //provided by setupapi.dll, which comes with Windows SetupDiGetDeviceInterfaceDetail() //provided by setupapi.dll, which comes with Windows SetupDiGetDeviceRegistryProperty() //provided by setupapi.dll, which comes with Windows malloc() //part of C runtime library, msvcrt.dll? CreateFile() //provided by kernel32.dll, which comes with Windows We will also be using the following unusual data types and structures. Documentation can also be found in the MSDN library: PSP_DEVICE_INTERFACE_DATA PSP_DEVICE_INTERFACE_DETAIL_DATA SP_DEVINFO_DATA HDEVINFO HANDLE GUID The ultimate objective of the following code is to call CreateFile(), which opens a communications pipe to a specific device (such as a HID class USB device endpoint). CreateFile() returns a "handle" which is needed later when calling ReadFile() or WriteFile(). These functions are used to actually send and receive application related data to/from the USB peripheral device. However, in order to call CreateFile(), we first need to get the device path for the USB device with the correct VID and PID. Getting the device path is a multi-step round about process, which requires calling several of the SetupDixxx() functions provided by setupapi.dll. */ //Globally Unique Identifier (GUID) for HID class devices. Windows uses GUIDs to identify things. GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, 0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30}; HDEVINFO DeviceInfoTable = INVALID_HANDLE_VALUE; PSP_DEVICE_INTERFACE_DATA InterfaceDataStructure = new SP_DEVICE_INTERFACE_DATA; PSP_DEVICE_INTERFACE_DETAIL_DATA DetailedInterfaceDataStructure = new SP_DEVICE_INTERFACE_DETAIL_DATA; SP_DEVINFO_DATA DevInfoData; DWORD InterfaceIndex = 0; DWORD StatusLastError = 0; DWORD dwRegType; DWORD dwRegSize; DWORD StructureSize = 0; PBYTE PropertyValueBuffer; bool MatchFound = false; DWORD ErrorStatus; String^ DeviceIDToFind = MY_DEVICE_ID; //First populate a list of plugged in devices (by specifying "DIGCF_PRESENT"), which are of the specified class GUID. DeviceInfoTable = SetupDiGetClassDevsUM(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); //Now look through the list we just populated. We are trying to see if any of them match our device. while(true) { InterfaceDataStructure->cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); if(SetupDiEnumDeviceInterfacesUM(DeviceInfoTable, NULL, &InterfaceClassGuid, InterfaceIndex, InterfaceDataStructure)) { ErrorStatus = GetLastError(); if(ERROR_NO_MORE_ITEMS == ErrorStatus) //Did we reach the end of the list of matching devices in the DeviceInfoTable? { //Cound not find the device. Must not have been attached. SetupDiDestroyDeviceInfoListUM(DeviceInfoTable); //Clean up the old structure we no longer need. return; } } else //Else some other kind of unknown error ocurred... { ErrorStatus = GetLastError(); SetupDiDestroyDeviceInfoListUM(DeviceInfoTable); //Clean up the old structure we no longer need. return; } //Now retrieve the hardware ID from the registry. The hardware ID contains the VID and PID, which we will then //check to see if it is the correct device or not. //Initialize an appropriate SP_DEVINFO_DATA structure. We need this structure for SetupDiGetDeviceRegistryProperty(). DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA); SetupDiEnumDeviceInfoUM(DeviceInfoTable, InterfaceIndex, &DevInfoData); //First query for the size of the hardware ID, so we can know how big a buffer to allocate for the data. SetupDiGetDeviceRegistryPropertyUM(DeviceInfoTable, &DevInfoData, SPDRP_HARDWAREID, &dwRegType, NULL, 0, &dwRegSize); //Allocate a buffer for the hardware ID. PropertyValueBuffer = (BYTE *) malloc (dwRegSize); if(PropertyValueBuffer == NULL) //if null, error, couldn't allocate enough memory { //Can't really recover from this situation, just exit instead. SetupDiDestroyDeviceInfoListUM(DeviceInfoTable); //Clean up the old structure we no longer need. return; } //Retrieve the hardware IDs for the current device we are looking at. PropertyValueBuffer gets filled with a //REG_MULTI_SZ (array of null terminated strings). To find a device, we only care about the very first string in the //buffer, which will be the "device ID". The device ID is a string which contains the VID and PID, in the example //format "Vid_04d8&Pid_003f". SetupDiGetDeviceRegistryPropertyUM(DeviceInfoTable, &DevInfoData, SPDRP_HARDWAREID, &dwRegType, PropertyValueBuffer, dwRegSize, NULL); //Now check if the first string in the hardware ID matches the device ID of my USB device. #ifdef UNICODE String^ DeviceIDFromRegistry = gcnew String((wchar_t *)PropertyValueBuffer); #else String^ DeviceIDFromRegistry = gcnew String((char *)PropertyValueBuffer); #endif free(PropertyValueBuffer); //No longer need the PropertyValueBuffer, free the memory to prevent potential memory leaks textBox1->Text = "Searching Device!"; //Convert both strings to lower case. This makes the code more robust/portable accross OS Versions DeviceIDFromRegistry = DeviceIDFromRegistry->ToLowerInvariant(); DeviceIDToFind = DeviceIDToFind->ToLowerInvariant(); //Now check if the hardware ID we are looking at contains the correct VID/PID MatchFound = DeviceIDFromRegistry->Contains(DeviceIDToFind); if(MatchFound == true) { //Device must have been found. Open read and write handles. In order to do this, we will need the actual device path first. //We can get the path by calling SetupDiGetDeviceInterfaceDetail(), however, we have to call this function twice: The first //time to get the size of the required structure/buffer to hold the detailed interface data, then a second time to actually //get the structure (after we have allocated enough memory for the structure.) DetailedInterfaceDataStructure->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); //First call populates "StructureSize" with the correct value SetupDiGetDeviceInterfaceDetailUM(DeviceInfoTable, InterfaceDataStructure, NULL, NULL, &StructureSize, NULL); DetailedInterfaceDataStructure = (PSP_DEVICE_INTERFACE_DETAIL_DATA)(malloc(StructureSize)); //Allocate enough memory if(DetailedInterfaceDataStructure == NULL) //if null, error, couldn't allocate enough memory { //Can't really recover from this situation, just exit instead. SetupDiDestroyDeviceInfoListUM(DeviceInfoTable); //Clean up the old structure we no longer need. return; } DetailedInterfaceDataStructure->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); //Now call SetupDiGetDeviceInterfaceDetail() a second time to receive the goods. SetupDiGetDeviceInterfaceDetailUM(DeviceInfoTable, InterfaceDataStructure, DetailedInterfaceDataStructure, StructureSize, NULL, NULL); //We now have the proper device path, and we can finally open read and write handles to the device. //We store the handles in the global variables "WriteHandle" and "ReadHandle", which we will use later to actually communicate. WriteHandle = CreateFile((DetailedInterfaceDataStructure->DevicePath), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); ErrorStatus = GetLastError(); if(ErrorStatus == ERROR_SUCCESS) textBox1->Text = "Write Enabled."; //Make button no longer greyed out ReadHandle = CreateFile((DetailedInterfaceDataStructure->DevicePath), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); ErrorStatus = GetLastError(); if(ErrorStatus == ERROR_SUCCESS) { textBox1->Text = "Read Enabled."; //Make label no longer greyed out } SetupDiDestroyDeviceInfoListUM(DeviceInfoTable); //Clean up the old structure we no longer need. //追加修正部 // 接続確認メッセージ準備 OutputPacketBuffer[0] = 0; // Report ID OutputPacketBuffer[1] = 0x30; // 接続確認 // 接続確認 コマンド送信 WriteFile(WriteHandle, &OutputPacketBuffer, 65, &BytesWritten, 0); // OK受信 ReadFile(ReadHandle, &InputPacketBuffer, 65, &BytesRead, 0); // OK確認 if((InputPacketBuffer[3] == 0x4F)&&(InputPacketBuffer[4] == 0x4B)) //"OK" //O: 0x4F //K: 0x4B textBox1->Text = "接続完了!!"; // 接続正常完了 // 接続完了でタイマ1スタート timer1->Interval = 100; timer1->Enabled = TRUE; return; } InterfaceIndex++; //Keep looping until we either find a device with matching VID and PID, or until we run out of items. }//end of while(true) //-------------------------------------------------------END CUT AND PASTE BLOCK------------------------------------------------------------------------------------- } private: System::Void Led0Btn_Click(System::Object^ sender, System::EventArgs^ e) { OutputPacketBuffer[0] = 0; // Report ID OutputPacketBuffer[1] = 0x80; // LED制御コマンド OutputPacketBuffer[2] = 0x30; // LED0アドレス if(Led0Mode == 0) //消灯していたなら { Led0Mode = 1; OutputPacketBuffer[3] = 0x31; // Led点灯 //0x31 = '1' } else { Led0Mode = 0; OutputPacketBuffer[3] = 0x30; // Led消灯 //0x30 = '0' } // 送信 WriteFile(WriteHandle, &OutputPacketBuffer, 65, &BytesWritten, 0); // 受信 ReadFile(ReadHandle, &InputPacketBuffer, 65, &BytesRead, 0); if(InputPacketBuffer[3] == 0x31)Led0Btn->BackColor =Color:: Red; // オンなら赤 else Led0Btn->BackColor = Color::Gray; // オフなら灰色 } private: System::Void Led1Btn_Click(System::Object^ sender, System::EventArgs^ e) { OutputPacketBuffer[0] = 0; // Report ID OutputPacketBuffer[1] = 0x80; // LED制御コマンド OutputPacketBuffer[2] = 0x31; // LED1アドレス if(Led1Mode == 0) //消灯していたなら { Led1Mode = 1; OutputPacketBuffer[3] = 0x31; // Led点灯 //0x31 = '1' } else { Led1Mode = 0; OutputPacketBuffer[3] = 0x30; // Led消灯 //0x30 = '0' } // 送信 WriteFile(WriteHandle, &OutputPacketBuffer, 65, &BytesWritten, 0); // 受信 ReadFile(ReadHandle, &InputPacketBuffer, 65, &BytesRead, 0); if(InputPacketBuffer[4] == 0x31)Led1Btn->BackColor =Color:: Red; // オンなら赤 else Led1Btn->BackColor = Color::Gray; // オフなら灰色 } private: System::Void Led2Btn_Click(System::Object^ sender, System::EventArgs^ e) { OutputPacketBuffer[0] = 0; // Report ID OutputPacketBuffer[1] = 0x80; // LED制御コマンド OutputPacketBuffer[2] = 0x32; // LED1アドレス if(Led2Mode == 0) //消灯していたなら { Led2Mode = 1; OutputPacketBuffer[3] = 0x31; // Led点灯 //0x31 = '1' } else { Led2Mode = 0; OutputPacketBuffer[3] = 0x30; // Led消灯 //0x30 = '0' } // 送信 WriteFile(WriteHandle, &OutputPacketBuffer, 65, &BytesWritten, 0); // 受信 ReadFile(ReadHandle, &InputPacketBuffer, 65, &BytesRead, 0); if(InputPacketBuffer[5] == 0x31)Led2Btn->BackColor =Color:: Red; // オンなら赤 else Led2Btn->BackColor = Color::Gray; // オフなら灰色 } private: System::Void Led3Btn_Click(System::Object^ sender, System::EventArgs^ e) { OutputPacketBuffer[0] = 0; // Report ID OutputPacketBuffer[1] = 0x80; // LED制御コマンド OutputPacketBuffer[2] = 0x33; // LED1アドレス if(Led3Mode == 0) //消灯していたなら { Led3Mode = 1; OutputPacketBuffer[3] = 0x31; // Led点灯 //0x31 = '1' } else { Led3Mode = 0; OutputPacketBuffer[3] = 0x30; // Led消灯 //0x30 = '0' } // 送信 WriteFile(WriteHandle, &OutputPacketBuffer, 65, &BytesWritten, 0); // 受信 ReadFile(ReadHandle, &InputPacketBuffer, 65, &BytesRead, 0); if(InputPacketBuffer[6] == 0x31)Led3Btn->BackColor =Color:: Red; // オンなら赤 else Led3Btn->BackColor = Color::Gray; // オフなら灰色 } private: System::Void timer1_Tick(System::Object^ sender, System::EventArgs^ e) { unsigned int ADCValue; double Volt; //可変抵抗器出力電圧のAD変換値 読込 OutputPacketBuffer[0] = 0; // Report ID OutputPacketBuffer[1] = 0x82; // 可変抵抗器outのAD変換値読込 OutputPacketBuffer[2] = 0x31; // VR1 WriteFile(WriteHandle, &OutputPacketBuffer, 65, &BytesWritten, 0); //対応するエンドポイント //InputPacketBuffer[3](Windows側)vs ToSendDataBuffer[2](PIC側) //InputPacketBuffer[4](Windows側)vs ToSendDataBuffer[3](PIC側) ReadFile(ReadHandle, &InputPacketBuffer, 65, &BytesRead, 0); ADCValue = (InputPacketBuffer[4] * 256) + InputPacketBuffer[3]; Volt = (ADCValue * 5.0) / 1024; textBox2->Text = String::Format("{0:F2} [V]",Volt); //小数点以下2桁にして表示 //スイッチの状態読込 OutputPacketBuffer[0] = 0; // Report ID OutputPacketBuffer[1] = 0x81; // SW状態要求コマンド OutputPacketBuffer[2] = 0x31; // SW1 WriteFile(WriteHandle, &OutputPacketBuffer, 65, &BytesWritten, 0); ReadFile(ReadHandle, &InputPacketBuffer, 65, &BytesRead, 0); //ovalShape: FillStyleのpropertyはSolidに設定のこと//コマンドでは設定できない!! if(InputPacketBuffer[3] == 0x30) // オンか? ovalShape1->FillColor = Color::Orange; //ON ならオレンジ色 else ovalShape1->FillColor = Color::Gray; // OFFなら灰色 //ovalShape: FillStyleのpropertyはSolidに設定のこと//コマンドでは設定できない!! OutputPacketBuffer[2] = 0x32; // SW2 WriteFile(WriteHandle, &OutputPacketBuffer, 65, &BytesWritten, 0); ReadFile(ReadHandle, &InputPacketBuffer, 65, &BytesRead, 0); if(InputPacketBuffer[3] == 0x30) // オンか? ovalShape2->FillColor = Color::Orange; //ON ならオレンジ色 else ovalShape2->FillColor = Color::Gray; // OFFなら灰色 //ovalShape: FillStyleのpropertyはSolidに設定のこと//コマンドでは設定できない!! OutputPacketBuffer[2] = 0x33; // SW3 WriteFile(WriteHandle, &OutputPacketBuffer, 65, &BytesWritten, 0); ReadFile(ReadHandle, &InputPacketBuffer, 65, &BytesRead, 0); if(InputPacketBuffer[3] == 0x30) // オンか? ovalShape3->FillColor = Color::Orange; //ON ならオレンジ色 else ovalShape3->FillColor = Color::Gray; // OFFなら灰色 } }; } } |
<実行結果>
PC画面のLED0,LED2、LED3ボタンをクリックして キバン上のLEDを点灯させたあと、キバン上のSW1とSW3をおした時のPCのスクリーンショット画像とキバンの写真です。
ADコンバータで検出された電圧が ダイアログのテキストボックスに小数点以下2桁で3.53[V}と表示されています。
パソコン画面の ダイアログ |
|
PIC32MX795F512L キバン上で SW1 とSW3を押したところ |
■ PIC32MX795F512L HIDクラス 漢字を含む文字列の送受信
PIC32MX795F512Lにキャラクタ液晶を接続した USB通信 送受信の例を紹介します。 → PIC側ハード&ソフト
以下のソフトは、 マイクロチップ社のライブラリ microchip_solutions_v2013-06-15\USB\Simple Demo
- Windows Software \Microsoft Visual C++ 2005 Expressに収納されているVC++Cのサンプルプログラムを参考にして作成したものです。
<試作品仕様>
・PC側からデータをPIC側にUSB HIDクラス通信で送信する。
・送信文字コードはシフトJISを使用する
・PIC側では受信した文字列を液晶上段に、受信データに基づき返信したデータを液晶下段に表示する。
・PC側でも受信したデータをリストボックスに表示する。
・PC側からの送信データ 及びPIC側からの返信データは以下とする。
@ U.K. → London
A America → Washington
B Japan → 0x938c(東)0x8b9e(京)
C 中(0x9286)国 (0x8D91) → 北(0x966B)京(0x8B9E)
D I am a boy. → Pardon ?
<プログラム例> //Form1.h #pragma once //Includes #include <Windows.h> //Definitions for various common and not so common types like DWORD, PCHAR, HANDLE, etc. #include <setupapi.h> //From Platform SDK. Definitions needed for the SetupDixxx() functions, which we use to //find our plug and play device. #include <Winusb.h> // WINUSB_INTERFACE_HANDLE(インターフェース用ハンドル)が定義されている #include <msclr/marshal.h> //マーシャリング関係関数用 //Modify this value to match the VID and PID in your USB device descriptor. //Use the formatting: "Vid_xxxx&Pid_xxxx" where xxxx is a 16-bit hexadecimal number. #define MY_DEVICE_ID "Vid_04d8&Pid_003F" //VID = 0x04D8 PID = 0x003F namespace CWhiteForm { using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; using namespace System::Text;//エンコードクラスを使用する場合要追加 using namespace msclr::interop; //マーシャリング関係関数用 //-------------------------------------------------------BEGIN CUT AND PASTE BLOCK----------------------------------------------------------------------------------- using namespace System::Runtime::InteropServices; //Need this to support "unmanaged" code. /* In order to use these unmanaged functions from within the managed .NET environment, we need to explicitly import the functions which we will be using from other .DLL file(s). Simply including the appropriate header files is not enough. Note: In order to avoid potential name conflicts in the header files (which we still use), I have renamed the functions by adding "UM" (unmanaged) onto the end of them. To find documentation for the functions in MSDN, search for the function name without the extra "UM" attached. Note2: In the header files (such as setupapi.h), normally the function names are remapped, depending upon if UNICODE is defined or not. For example, two versions of the function SetupDiGetDeviceInterfaceDetail() exist. One for UNICODE, and one for ANSI. If the wrong version of the function is called, things won't work correctly. Therefore, in order to make sure the correct one gets called (based on your compiler settings, which may or may not define "UNICODE"), it is useful to explicity specify the CharSet when doing the DLL import. */ #ifdef UNICODE #define Seeifdef Unicode #else #define Seeifdef Ansi #endif //使用するDLL関数の宣言 //Returns a HDEVINFO type for a device information set (USB HID devices in //our case). We will need the HDEVINFO as in input parameter for calling many of //the other SetupDixxx() functions. [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiGetClassDevs", CallingConvention=CallingConvention::Winapi)] extern "C" HDEVINFO SetupDiGetClassDevsUM( LPGUID ClassGuid, //Input: Supply the class GUID here. PCTSTR Enumerator, //Input: Use NULL here, not important for our purposes HWND hwndParent, //Input: Use NULL here, not important for our purposes DWORD Flags); //Input: Flags describing what kind of filtering to use. //Gives us "PSP_DEVICE_INTERFACE_DATA" which contains the Interface specific GUID (different //from class GUID). We need the interface GUID to get the device path. [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiEnumDeviceInterfaces", CallingConvention=CallingConvention::Winapi)] extern "C" WINSETUPAPI BOOL WINAPI SetupDiEnumDeviceInterfacesUM( HDEVINFO DeviceInfoSet, //Input: Give it the HDEVINFO we got from SetupDiGetClassDevs() PSP_DEVINFO_DATA DeviceInfoData, //Input (optional) LPGUID InterfaceClassGuid, //Input DWORD MemberIndex, //Input: "Index" of the device you are interested in getting the path for. PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData);//Output: This function fills in an "SP_DEVICE_INTERFACE_DATA" structure. //SetupDiDestroyDeviceInfoList() frees up memory by destroying a DeviceInfoList [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiDestroyDeviceInfoList", CallingConvention=CallingConvention::Winapi)] extern "C" WINSETUPAPI BOOL WINAPI SetupDiDestroyDeviceInfoListUM( HDEVINFO DeviceInfoSet); //Input: Give it a handle to a device info list to deallocate from RAM. //SetupDiEnumDeviceInfo() fills in an "SP_DEVINFO_DATA" structure, which we need for SetupDiGetDeviceRegistryProperty() [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiEnumDeviceInfo", CallingConvention=CallingConvention::Winapi)] extern "C" WINSETUPAPI BOOL WINAPI SetupDiEnumDeviceInfoUM( HDEVINFO DeviceInfoSet, DWORD MemberIndex, PSP_DEVINFO_DATA DeviceInfoData); //SetupDiGetDeviceRegistryProperty() gives us the hardware ID, which we use to check to see if it has matching VID/PID [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiGetDeviceRegistryProperty", CallingConvention=CallingConvention::Winapi)] extern "C" WINSETUPAPI BOOL WINAPI SetupDiGetDeviceRegistryPropertyUM( HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, DWORD Property, PDWORD PropertyRegDataType, PBYTE PropertyBuffer, DWORD PropertyBufferSize, PDWORD RequiredSize); //SetupDiGetDeviceInterfaceDetail() gives us a device path, which is needed before CreateFile() can be used. [DllImport("setupapi.dll" , CharSet = CharSet::Seeifdef, EntryPoint="SetupDiGetDeviceInterfaceDetail", CallingConvention=CallingConvention::Winapi)] extern "C" BOOL SetupDiGetDeviceInterfaceDetailUM( HDEVINFO DeviceInfoSet, //Input: Wants HDEVINFO which can be obtained from SetupDiGetClassDevs() PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, //Input: Pointer to an structure which defines the device interface. PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData, //Output: Pointer to a strucutre, which will contain the device path. DWORD DeviceInterfaceDetailDataSize, //Input: Number of bytes to retrieve. PDWORD RequiredSize, //Output (optional): Te number of bytes needed to hold the entire struct PSP_DEVINFO_DATA DeviceInfoData); //Output //USBデバイスとのパイプ(エンドポイント)に書き込む関数 //WinUsb_WritePipe() is the basic function used to write data to the USB device (sends data to OUT endpoints on the device) [DllImport("winusb.dll" , CharSet = CharSet::Seeifdef, EntryPoint="WinUsb_WritePipe")] extern "C" BOOL WinUsb_WritePipe( WINUSB_INTERFACE_HANDLE InterfaceHandle, UCHAR PipeID, PUCHAR Buffer, ULONG BufferLength, PULONG LengthTransferred, LPOVERLAPPED Overlapped); //USBデバイスとのパイプ(エンドポイント)からデータを読み込む関数 //WinUsb_ReadPipe() is the basic function used to read data from the USB device (polls for and obtains data from //IN endpoints on the device) [DllImport("winusb.dll" , CharSet = CharSet::Seeifdef, EntryPoint="WinUsb_ReadPipe")] extern "C" BOOL WinUsb_ReadPipe( WINUSB_INTERFACE_HANDLE InterfaceHandle, //The interface handle that WinUsb_Initialize returned. UCHAR PipeID, //An 8-bit value that consists of a 7-bit address and a direction bit. //This parameter corresponds to the bEndpointAddress field in the endpoint descriptor. PUCHAR Buffer, //A caller-allocated buffer that receives the data that is read. ULONG BufferLength, //The maximum number of bytes to read. This number must be less than or equal to the size, in bytes, of Buffer. PULONG LengthTransferred, //A pointer to a ULONG variable that receives the actual number of bytes that were copied into Buffer. LPOVERLAPPED Overlapped); //An optional pointer to an OVERLAPPED structure that is used for asynchronous operations. If this //parameter is specified, WinUsb_ReadPipe returns immediately rather than waiting synchronously //for the operation to complete before returning. An event is signaled when the operation is complete. // Variables that need to have wide scope. HANDLE WriteHandle = INVALID_HANDLE_VALUE; //Need to get a write "handle" to our device before we can write to it. HANDLE ReadHandle = INVALID_HANDLE_VALUE; //Need to get a read "handle" to our device before we can read from it. //-------------------------------------------------------END CUT AND PASTE BLOCK------------------------------------------------------------------------------------- //グローバル変数定義 DWORD BytesWritten = 0; //書き込んだバイト数 DWORD BytesRead = 0; //読み込んだバイト数 unsigned char OutputPacketBuffer[65]; //Allocate a memory buffer equal to our endpoint size + 1 unsigned char InputPacketBuffer[65]; //グローバル変数 HANDLE MyDeviceHandle = INVALID_HANDLE_VALUE; //USBデバイス用のハンドル WINUSB_INTERFACE_HANDLE MyWinUSBInterfaceHandle; //インターフェース用ハンドル //And then can call WinUsb_Initialize() to get the interface handle //which is needed for doing other operations with the device (like //reading and writing to the USB device). /// <summary> /// Form1 の概要 /// </summary> public ref class Form1 : public System::Windows::Forms::Form { public: Form1(void) { InitializeComponent(); comboBox1->Items->Clear(); comboBox1->Items->Insert(0,"U.K. "); comboBox1->Items->Insert(1,"America "); comboBox1->Items->Insert(2,"Japan "); comboBox1->Items->Insert(3,"中国 "); //comboBox1->Items->Insert(3,"Chuugoku "); comboBox1->Items->Insert(4,"I am a boy. "); comboBox1->SelectedIndex = 0; SendBtn->Enabled = false; //送信ボタンをディスイネーブル化 textBox1->Text = "未接続"; textBox1->ForeColor = Color::Red; } protected: /// <summary> /// 使用中のリソースをすべてクリーンアップします。 /// </summary> ~Form1() { if (components) { delete components; } } #pragma endregion private: System::Void ConnectBtn_Click(System::Object^ sender, System::EventArgs^ e) { //-------------------------------------------------------BEGIN CUT AND PASTE BLOCK----------------------------------------------------------------------------------- /* Before we can "connect" our application to our USB embedded device, we must first find the device. A USB bus can have many devices simultaneously connected, so somehow we have to find our device, and only our device. This is done with the Vendor ID (VID) and Product ID (PID). Each USB product line should have a unique combination of VID and PID. Microsoft has created a number of functions which are useful for finding plug and play devices. Documentation for each function used can be found in the MSDN library. We will be using the following functions: SetupDiGetClassDevs() //provided by setupapi.dll, which comes with Windows SetupDiEnumDeviceInterfaces() //provided by setupapi.dll, which comes with Windows GetLastError() //provided by kernel32.dll, which comes with Windows SetupDiDestroyDeviceInfoList() //provided by setupapi.dll, which comes with Windows SetupDiGetDeviceInterfaceDetail() //provided by setupapi.dll, which comes with Windows SetupDiGetDeviceRegistryProperty() //provided by setupapi.dll, which comes with Windows malloc() //part of C runtime library, msvcrt.dll? CreateFile() //provided by kernel32.dll, which comes with Windows We will also be using the following unusual data types and structures. Documentation can also be found in the MSDN library: PSP_DEVICE_INTERFACE_DATA PSP_DEVICE_INTERFACE_DETAIL_DATA SP_DEVINFO_DATA HDEVINFO HANDLE GUID The ultimate objective of the following code is to call CreateFile(), which opens a communications pipe to a specific device (such as a HID class USB device endpoint). CreateFile() returns a "handle" which is needed later when calling ReadFile() or WriteFile(). These functions are used to actually send and receive application related data to/from the USB peripheral device. However, in order to call CreateFile(), we first need to get the device path for the USB device with the correct VID and PID. Getting the device path is a multi-step round about process, which requires calling several of the SetupDixxx() functions provided by setupapi.dll. */ //Globally Unique Identifier (GUID) for HID class devices. Windows uses GUIDs to identify things. GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, 0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30}; HDEVINFO DeviceInfoTable = INVALID_HANDLE_VALUE; PSP_DEVICE_INTERFACE_DATA InterfaceDataStructure = new SP_DEVICE_INTERFACE_DATA; PSP_DEVICE_INTERFACE_DETAIL_DATA DetailedInterfaceDataStructure = new SP_DEVICE_INTERFACE_DETAIL_DATA; SP_DEVINFO_DATA DevInfoData; DWORD InterfaceIndex = 0; DWORD StatusLastError = 0; DWORD dwRegType; DWORD dwRegSize; DWORD StructureSize = 0; PBYTE PropertyValueBuffer; bool MatchFound = false; DWORD ErrorStatus; String^ DeviceIDToFind = MY_DEVICE_ID; //First populate a list of plugged in devices (by specifying "DIGCF_PRESENT"), which are of the specified class GUID. DeviceInfoTable = SetupDiGetClassDevsUM(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); //Now look through the list we just populated. We are trying to see if any of them match our device. while(true) { InterfaceDataStructure->cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); if(SetupDiEnumDeviceInterfacesUM(DeviceInfoTable, NULL, &InterfaceClassGuid, InterfaceIndex, InterfaceDataStructure)) { ErrorStatus = GetLastError(); if(ERROR_NO_MORE_ITEMS == ErrorStatus) //Did we reach the end of the list of matching devices in the DeviceInfoTable? { //Cound not find the device. Must not have been attached. SetupDiDestroyDeviceInfoListUM(DeviceInfoTable); //Clean up the old structure we no longer need. return; } } else //Else some other kind of unknown error ocurred... { ErrorStatus = GetLastError(); SetupDiDestroyDeviceInfoListUM(DeviceInfoTable); //Clean up the old structure we no longer need. return; } //Now retrieve the hardware ID from the registry. The hardware ID contains the VID and PID, which we will then //check to see if it is the correct device or not. //Initialize an appropriate SP_DEVINFO_DATA structure. We need this structure for SetupDiGetDeviceRegistryProperty(). DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA); SetupDiEnumDeviceInfoUM(DeviceInfoTable, InterfaceIndex, &DevInfoData); //First query for the size of the hardware ID, so we can know how big a buffer to allocate for the data. SetupDiGetDeviceRegistryPropertyUM(DeviceInfoTable, &DevInfoData, SPDRP_HARDWAREID, &dwRegType, NULL, 0, &dwRegSize); //Allocate a buffer for the hardware ID. PropertyValueBuffer = (BYTE *) malloc (dwRegSize); if(PropertyValueBuffer == NULL) //if null, error, couldn't allocate enough memory { //Can't really recover from this situation, just exit instead. SetupDiDestroyDeviceInfoListUM(DeviceInfoTable); //Clean up the old structure we no longer need. return; } //Retrieve the hardware IDs for the current device we are looking at. PropertyValueBuffer gets filled with a //REG_MULTI_SZ (array of null terminated strings). To find a device, we only care about the very first string in the //buffer, which will be the "device ID". The device ID is a string which contains the VID and PID, in the example //format "Vid_04d8&Pid_003f". SetupDiGetDeviceRegistryPropertyUM(DeviceInfoTable, &DevInfoData, SPDRP_HARDWAREID, &dwRegType, PropertyValueBuffer, dwRegSize, NULL); //Now check if the first string in the hardware ID matches the device ID of my USB device. #ifdef UNICODE String^ DeviceIDFromRegistry = gcnew String((wchar_t *)PropertyValueBuffer); #else String^ DeviceIDFromRegistry = gcnew String((char *)PropertyValueBuffer); #endif free(PropertyValueBuffer); //No longer need the PropertyValueBuffer, free the memory to prevent potential memory leaks textBox1->Text = "Searching Device!"; //Convert both strings to lower case. This makes the code more robust/portable accross OS Versions DeviceIDFromRegistry = DeviceIDFromRegistry->ToLowerInvariant(); DeviceIDToFind = DeviceIDToFind->ToLowerInvariant(); //Now check if the hardware ID we are looking at contains the correct VID/PID MatchFound = DeviceIDFromRegistry->Contains(DeviceIDToFind); if(MatchFound == true) { //Device must have been found. Open read and write handles. In order to do this, we will need the actual device path first. //We can get the path by calling SetupDiGetDeviceInterfaceDetail(), however, we have to call this function twice: The first //time to get the size of the required structure/buffer to hold the detailed interface data, then a second time to actually //get the structure (after we have allocated enough memory for the structure.) DetailedInterfaceDataStructure->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); //First call populates "StructureSize" with the correct value SetupDiGetDeviceInterfaceDetailUM(DeviceInfoTable, InterfaceDataStructure, NULL, NULL, &StructureSize, NULL); DetailedInterfaceDataStructure = (PSP_DEVICE_INTERFACE_DETAIL_DATA)(malloc(StructureSize)); //Allocate enough memory if(DetailedInterfaceDataStructure == NULL) //if null, error, couldn't allocate enough memory { //Can't really recover from this situation, just exit instead. SetupDiDestroyDeviceInfoListUM(DeviceInfoTable); //Clean up the old structure we no longer need. return; } DetailedInterfaceDataStructure->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); //Now call SetupDiGetDeviceInterfaceDetail() a second time to receive the goods. SetupDiGetDeviceInterfaceDetailUM(DeviceInfoTable, InterfaceDataStructure, DetailedInterfaceDataStructure, StructureSize, NULL, NULL); //We now have the proper device path, and we can finally open read and write handles to the device. //We store the handles in the global variables "WriteHandle" and "ReadHandle", which we will use later to actually communicate. WriteHandle = CreateFile((DetailedInterfaceDataStructure->DevicePath), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); ErrorStatus = GetLastError(); if(ErrorStatus == ERROR_SUCCESS) textBox1->Text = "Write Enabled."; //Make button no longer greyed out ReadHandle = CreateFile((DetailedInterfaceDataStructure->DevicePath), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); ErrorStatus = GetLastError(); if(ErrorStatus == ERROR_SUCCESS) { textBox1->Text = "Read Enabled."; //Make label no longer greyed out } SetupDiDestroyDeviceInfoListUM(DeviceInfoTable); //Clean up the old structure we no longer need. //追加修正部 // 接続確認メッセージ準備 OutputPacketBuffer[0] = 0; // Report ID OutputPacketBuffer[1] = 0x30; // 接続確認 // 接続確認 コマンド送信 WriteFile(WriteHandle, &OutputPacketBuffer, 65, &BytesWritten, 0); // OK受信 ReadFile(ReadHandle, &InputPacketBuffer, 65, &BytesRead, 0); // OK確認 if((InputPacketBuffer[3] == 0x4F)&&(InputPacketBuffer[4] == 0x4B)) //"OK" //O: 0x4F //K: 0x4B textBox1->Text = "接続完了!!"; // 接続正常完了 textBox1->ForeColor = Color::Blue; //文字色→青色 listBox1->Items->Clear(); SendBtn->Enabled = true; //送信ボタン→イネーブル return; } InterfaceIndex++; //Keep looping until we either find a device with matching VID and PID, or until we run out of items. }//end of while(true) //-------------------------------------------------------END CUT AND PASTE BLOCK------------------------------------------------------------------------------------- } private: System::Void SendBtn_Click(System::Object^ sender, System::EventArgs^ e) { int i; int iy; array <Byte>^ bytesArry = gcnew array<Byte>(65); //マネージ形式の配列の宣言 Encoding^ encSjis = Encoding::GetEncoding("shift-jis"); //シフトJISのエンコードクラスを宣言 & インスタンス生成 Encoding^ encUni = Encoding::GetEncoding("unicode"); //ユニコード(UCS-16)のエンコードクラスを宣言 & インスタンス生成 String^ strSend = comboBox1->Text; richTextBox1-> Clear(); richTextBox2-> Clear(); richTextBox3-> Clear(); if (!strSend->EndsWith("\r")) strSend += "\r"; //改行が無かったら \rを追加する bytesArry = encSjis->GetBytes( strSend ); //'送信文字をunicode→Shift-jisに変換してをByte配列に格納 for(i = 2; i < bytesArry->Length - 1 ; i++)OutputPacketBuffer[i] = bytesArry[i - 2]; //マネージ形式の配列をC言語の配列に変換 OutputPacketBuffer[0] = 0; // Report ID OutputPacketBuffer[1] = 0x80; // 制御コマンド //リッチテキスト array<String^>^ mBX = gcnew array<String^>(65); //アスキー文字のメモリ //マネージ配列を定義 array<String^>^ mBX16 = gcnew array<String^>(65); //16進数のメモリ //マネージ配列を定義 iy = 0; for each(char s in OutputPacketBuffer) //charの配列BX[64]すべてに対して実行 { char ch = (char)OutputPacketBuffer[iy]; char* ptr; ptr = &ch; mBX[iy] = marshal_as<String^>(ptr); //配列の値をマーシャリング int Ix = (int)OutputPacketBuffer[iy]; mBX16[iy] = Convert::ToString(Ix,16); //整数を16進表示の文字列に変換する richTextBox1->SelectedText = //リッチテキストに表示 String::Format("OutBuf[{0}]: {1}, {2} 0x{3}\r",iy,mBX[iy],Ix,mBX16[iy]); iy++; } // 送信 WriteFile(WriteHandle, &OutputPacketBuffer, 65, &BytesWritten, 0); // 受信 ReadFile(ReadHandle, &InputPacketBuffer, 65, &BytesRead, 0); for(i = 0; i < 17; i++) { bytesArry[i] = InputPacketBuffer[i + 1]; //マネージ型配列作成 } richTextBox2->SelectedText = String::Format("InputBuffer[0]={0:X} ",InputPacketBuffer[0]); //リッチテキストに表示 richTextBox2->SelectedText = String::Format("InputBuffer[1]={0:X} ",InputPacketBuffer[1]); richTextBox2->SelectedText = String::Format("InputBuffer[2]={0:X} ",InputPacketBuffer[2]); richTextBox2->SelectedText = String::Format("InputBuffer[3]={0:X} ",InputPacketBuffer[3]); richTextBox2->SelectedText = String::Format("InputBuffer[4]={0:X} ",InputPacketBuffer[4]); richTextBox2->SelectedText = String::Format("InputBuffer[5]={0:X} ",InputPacketBuffer[5]); richTextBox2->SelectedText = String::Format("InputBuffer[6]={0:X} ",InputPacketBuffer[6]); richTextBox2->SelectedText = String::Format("InputBuffer[7]={0:X} ",InputPacketBuffer[7]); richTextBox2->SelectedText = String::Format("InputBuffer[8]={0:X} ",InputPacketBuffer[8]); richTextBox2->SelectedText = String::Format("InputBuffer[9]={0:X} ",InputPacketBuffer[9]); richTextBox3->SelectedText = String::Format("bytesArry[0]={0:X} ",bytesArry[0]); //リッチテキストに表示 richTextBox3->SelectedText = String::Format("bytesArry[1]={0:X} ",bytesArry[1]); richTextBox3->SelectedText = String::Format("bytesArry[2]={0:X} ",bytesArry[2]); richTextBox3->SelectedText = String::Format("bytesArry[3]={0:X} ",bytesArry[3]); richTextBox3->SelectedText = String::Format("bytesArry[4]={0:X} ",bytesArry[4]); richTextBox3->SelectedText = String::Format("bytesArry[5]={0:X} ",bytesArry[5]); richTextBox3->SelectedText = String::Format("bytesArry[6]={0:X} ",bytesArry[6]); richTextBox3->SelectedText = String::Format("bytesArry[7]={0:X} ",bytesArry[7]); richTextBox3->SelectedText = String::Format("bytesArry[8]={0:X} ",bytesArry[8]); richTextBox3->SelectedText = String::Format("bytesArry[9]={0:X} ",bytesArry[9]); array<Byte>^byteUni = Encoding::Convert(encSjis, encUni, bytesArry); //shift-jis からunicodeに変換する String^ strUni = encUni->GetString(byteUni); //配列をunicodeの文字列に変換 listBox1->Items->Add(strUni); } }; } ....... ....... |
<実行結果>
PIC32MX795F512Lの液晶 上段: PCからの受信文字列 下段: PCへの返信文字列 |
PC画面 | |
USB接続が 未接続の状態 |
- |
|
接続ボタンをクリックして USB接続が 確立した状態 |
- |
|
コンボボックスから ”U.K.”を選択して送信し、 返信をリストボックスに 受信した状態 |
||
次に、コンボボックスから ”America”を選択して送信し、 返信をリストボックスに 受信した状態 |
||
次に、コンボボックスから ”Japan”を選択して送信し、 返信をリストボックスに 受信した状態 |
||
次に、コンボボックスから ”中国”を選択して送信し、 返信をリストボックスに 受信した状態 |
||
最後に、コンボボックスから ”I am a boy”を選択して送信し、 返信をリストボックスに 受信した状態 |
■ PIC32MZ2048EFH100
HIDクラス IO制御(LED/点灯消灯、ADコンバータ・スイッチ読込・表示
(サンプルプログラム(VC++プロジェクトファイル) → ダウンロード)
(サンプルプログラム(実行ファイル) → ダウンロード)
★ PIC側のハードとソフトは こちらを参照願います
<仕様>
・PCとPIC間のUSB通信はHIDクラスとする。
・PICはPIC32MZ2048EFH100を使用のこと。
・PC側のプログラムは、マイクロソフトのVC++で作成のこと。
・PC側はダイアログウィンドウを作成して、以下を実施できること。
@ PIC側のLEDのON/OFF制御
A PIC側のスイッチの状態のモニタ
B PIC側の可変抵抗器の電圧もモニタ
・ダイアログウィンドウのボタンスイッチ(コントロール)のクリックにより、PIC側のLEDがON/OFFできること。
・ボタンスイッチの背景色はLEDがONの場合は赤色、LEDがOFFの場合は灰色とする。
・ボタンコントロールはLEDがONの場合は赤色に、OFFの場合は灰色とする。
・LEDがONの場合は画像を またOFFの場合は画像を表示のこと。
・PIC側のスイッチのON/OFF状態が分かるモニターとして 円形のオーバルシェープコントロールを使用のこと。
尚、色はONの場合はオレンジ色、OFFの場合は灰色とする。
・スイッチがONの場合は画像を またOFFの場合は画像を表示のこと。画像は重ねて配置のこと。
・PIC側の可変抵抗器の電圧値に表示のこと。値は四捨五入して小数点以下2桁を表示のこと。
・入力電圧値がビジュアルにわかるようにプログレスバーでも表示のこと。
・途中切断も含め、USB通信がOFFの場合はLED,、スイッチ、可変抵抗器などに係る表示は行わないようにすること。
・PCのアプリケーションは100msec毎にPIC側の状態をサンプリングのこと。
・ PC側のダイアログウィンドウ例を以下に示す。
<USB通信OFFの場合> | <USB通信がONの場合> |
・PIC側のモニタ用液晶として、I2C制御のキャラクタ液晶を用いること。
・PICが起動する時、キャラクタ液晶には以下を表示のこと。
1行目: USB device HID
2行目: start !!
・USB通信が始まったら液晶に可変抵抗器の入力電圧をモニタする。
1行目: AdValue= ◯◯◯◯
2行目: Volt= ◯◯◯◯[V]
AdValueの〇〇〇〇は内臓ADコンバータの読み込み整数値、Voltは電圧換算値で小数点以下3桁を表示のこと。
・可変抵抗器の電圧のサンプリングタイムは50msecとする。
・USBケーブルが送受信中に抜かれたりして通信が切断された場合は LED、スイッチ、可変抵抗器のPictureBoxはDisableとなり ダイアログウィンドウ画面は 上記の <USB通信OFFの場合>の画面に戻ること。
以下、プログラム抜粋
//Form1.h デザイン |
|
<動作結果> (→ 動画:1080pのHD動画を見ることができます。)
■ PIC32MZ2048EFH100
CDCクラス 文字列送受信
(サンプルプログラム(VC++プロジェクトファイル) → ダウンロード)
(サンプルプログラム(実行ファイル) → ダウンロード)
★ PIC側のハードとソフトは、以下を参照願います
グラフィック液晶の ラベルウィジェットへの表示 |
プログラム名称 | URL | ||
有 | ラベルにPCからのUSB受信文字列を表示 |
URL | ||
無 | USB(デバイスモード)CDCクラスPIC−PC間 文字列送受信 |
URL |
<仕様>
・PCとPIC32MZ間でUSB接続による文字列の送受信をおこなう。
・USBのライブラリはHarmonyのCDCクラスをつかうこと
・PC側から文字列に対して PIC側からそれぞれに対応した文字列を返信すること
送受信の文字列は 以下とする
PCからの送信文字列 | PICからの返信文字列 | |
Japan\r | Tokyo | |
America\r | Washington | |
U.K.\r | London | |
France\r | Paris | |
How are you?\ | Pardon? |
・PICのキャラくタ液晶はI2Cインターフェースのキャラクタ液晶とし、以下を表示のこと。
1行目 …… PCからの受信文字列
2行目 …… PCへの送信文字列
・PC側のアプリケーションは マイクロソフトのVC#で作成のこと。
・PCのアプリケーションプログラムの基本動作は、以下とする。
尚、USBコネクタの脱着を検出し USB接続状態表示(接続完了/未接続 及びランプ表示の灰色/オレンジ色)に反映のこと。
以下、プログラム抜粋
//Form1.h デザイン |
//Form1.h |
#pragma once |
<動作結果>(→ 動画:1080pのHD動画を見ることができます。)